@mintplayer/ng-spark 22.30.0 → 22.32.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.
Files changed (84) hide show
  1. package/README.md +2 -2
  2. package/auth/README.md +216 -0
  3. package/fesm2022/mintplayer-ng-spark-auth-account.mjs +2023 -0
  4. package/fesm2022/mintplayer-ng-spark-auth-account.mjs.map +1 -0
  5. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +96 -0
  6. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
  7. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +200 -0
  8. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs.map +1 -0
  9. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +452 -0
  10. package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
  11. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +201 -0
  12. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs.map +1 -0
  13. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +196 -0
  14. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
  15. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +16 -0
  16. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
  17. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +18 -0
  18. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
  19. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +282 -0
  20. package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
  21. package/fesm2022/mintplayer-ng-spark-auth-models.mjs +114 -0
  22. package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
  23. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +43 -0
  24. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
  25. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +216 -0
  26. package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
  27. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +223 -0
  28. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
  29. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +194 -0
  30. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
  31. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +210 -0
  32. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
  33. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +218 -0
  34. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
  35. package/fesm2022/mintplayer-ng-spark-auth.mjs +48 -0
  36. package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -0
  37. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +294 -2
  38. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  39. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +389 -0
  40. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs.map +1 -0
  41. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +204 -0
  42. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs.map +1 -0
  43. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +486 -0
  44. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs.map +1 -0
  45. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +350 -0
  46. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs.map +1 -0
  47. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs +66 -0
  48. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs.map +1 -0
  49. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +9 -4
  50. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  51. package/fesm2022/mintplayer-ng-spark-pwa.mjs +162 -0
  52. package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -0
  53. package/fesm2022/mintplayer-ng-spark-services.mjs +11 -1
  54. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  55. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  56. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  57. package/package.json +99 -1
  58. package/types/mintplayer-ng-spark-auth-account.d.ts +255 -0
  59. package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +22 -0
  60. package/types/mintplayer-ng-spark-auth-confirm-email.d.ts +21 -0
  61. package/types/mintplayer-ng-spark-auth-core.d.ts +197 -0
  62. package/types/mintplayer-ng-spark-auth-external-login.d.ts +132 -0
  63. package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +18 -0
  64. package/types/mintplayer-ng-spark-auth-guards.d.ts +15 -0
  65. package/types/mintplayer-ng-spark-auth-interceptors.d.ts +2 -0
  66. package/types/mintplayer-ng-spark-auth-login.d.ts +46 -0
  67. package/types/mintplayer-ng-spark-auth-models.d.ts +489 -0
  68. package/types/mintplayer-ng-spark-auth-pipes.d.ts +8 -0
  69. package/types/mintplayer-ng-spark-auth-register.d.ts +21 -0
  70. package/types/mintplayer-ng-spark-auth-reset-password.d.ts +25 -0
  71. package/types/mintplayer-ng-spark-auth-routes.d.ts +154 -0
  72. package/types/mintplayer-ng-spark-auth-sign-in.d.ts +84 -0
  73. package/types/mintplayer-ng-spark-auth-two-factor.d.ts +23 -0
  74. package/types/mintplayer-ng-spark-auth.d.ts +24 -0
  75. package/types/mintplayer-ng-spark-client-operations.d.ts +78 -3
  76. package/types/mintplayer-ng-spark-identity-provider-connected-applications.d.ts +35 -0
  77. package/types/mintplayer-ng-spark-identity-provider-core.d.ts +144 -0
  78. package/types/mintplayer-ng-spark-identity-provider-developers.d.ts +55 -0
  79. package/types/mintplayer-ng-spark-identity-provider-management.d.ts +33 -0
  80. package/types/mintplayer-ng-spark-identity-provider.d.ts +64 -0
  81. package/types/mintplayer-ng-spark-pwa.d.ts +89 -0
  82. package/types/mintplayer-ng-spark-services.d.ts +10 -0
  83. package/types/mintplayer-ng-spark-shell.d.ts +2 -2
  84. package/types/mintplayer-ng-spark.d.ts +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-po-detail.mjs","names":[],"sources":["../../po-detail/src/spark-po-detail.component.ts","../../po-detail/src/spark-po-detail.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal, TemplateRef, Type, untracked } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';\nimport { ActivatedRoute, Router, RouterModule } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsContainerComponent } from '@mintplayer/ng-bootstrap/container';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective } from '@mintplayer/ng-bootstrap/tab-control';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SparkService, SparkLanguageService, SparkReturnNavigationService } from '@mintplayer/ng-spark/services';\nimport { SparkAttributePatch, SparkAttributeRefreshService, SparkClientMethodRegistry, SparkQueryRefreshService } from '@mintplayer/ng-spark/client-operations';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n AttributeValuePipe,\n RawAttributeValuePipe,\n AsDetailColumnsPipe,\n AsDetailCellValuePipe,\n ArrayValuePipe,\n ReferenceLinkRoutePipe,\n ReferenceChipsPipe,\n ParsedDatePipe,\n AsDetailRowRendererPipe,\n} from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkQueryCardComponent, SparkGridCellComponent } from '@mintplayer/ng-spark/grid';\nimport { SPARK_ATTRIBUTE_RENDERERS, SparkResolvedRowRenderer, rendererValue, rowRendererInputs, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport {\n AttributeGroup,\n AttributeTab,\n CustomActionDefinition,\n EntityAttributeDefinition,\n EntityType,\n LookupReference,\n PersistentObject,\n QueryResultItem,\n ShowedOn,\n hasShowedOnFlag,\n EntityPermissions,\n SparkDeletedFilter,\n builtInAction,\n confirmationText,\n filterDetailActions,\n isShowedOnDetail,\n subQueriesOf,\n} from '@mintplayer/ng-spark/models';\nimport {\n SPARK_DETAIL_ACTIONS,\n SPARK_DETAIL_PANELS,\n SparkDetailContext,\n orderSparkExtensions,\n parseSparkDeletedParam,\n} from '@mintplayer/ng-spark/panels';\nimport { combineLatest } from 'rxjs';\n\n@Component({\n selector: 'spark-po-detail',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, RouterModule, BsAlertComponent, BsBadgeComponent,BsCardComponent, BsCardHeaderComponent, BsContainerComponent, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsPriorityNavComponent, BsPriorityNavItemDirective, BsTableComponent, BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryCardComponent, SparkGridCellComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe, RawAttributeValuePipe, AsDetailColumnsPipe, AsDetailRowRendererPipe, AsDetailCellValuePipe, ArrayValuePipe, ReferenceLinkRoutePipe, ReferenceChipsPipe, ParsedDatePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-po-detail.component.html',\n styleUrl: './spark-po-detail.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkPoDetailComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly returnNavigation = inject(SparkReturnNavigationService);\n private readonly queryRefresh = inject(SparkQueryRefreshService);\n private readonly attributeRefresh = inject(SparkAttributeRefreshService);\n protected readonly lang = inject(SparkLanguageService);\n private readonly clientMethods = inject(SparkClientMethodRegistry);\n protected readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n\n /**\n * Add-on panels and action-bar buttons (#460, `SPARK_DETAIL_PANELS` / `SPARK_DETAIL_ACTIONS`).\n * Multi-provided, so History, SoftDelete and Moderation can each add theirs to the page\n * `sparkRoutes()` routes to, which is created by the router and takes no templates.\n */\n protected readonly detailPanels = orderSparkExtensions(inject(SPARK_DETAIL_PANELS, { optional: true }), p => p.order);\n protected readonly detailActions = orderSparkExtensions(inject(SPARK_DETAIL_ACTIONS, { optional: true }), a => a.priority ?? 60);\n\n showCustomActions = input(true);\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n extraContentTemplate = input<TemplateRef<{ $implicit: PersistentObject; entityType: EntityType }> | null>(null);\n\n /**\n * Header slots forwarded to every sub-query card on this page.\n *\n * A structural directive cannot cross a component boundary, and this component is created by\n * the router — in a default app there is no `<spark-po-detail>` tag to project content into.\n * So an app that wants slotted chrome on its sub-queries substitutes its own route component\n * via `SparkRouteConfig.poDetail` and forwards the templates through here. Forwarding a\n * `TemplateRef` as an input is already this component's idiom; see the two above.\n *\n * Each slot may target one query by alias, so a single forwarded set serves a page rendering\n * several sub-queries.\n */\n queryIconTemplate = input<TemplateRef<any> | null>(null);\n queryCaptionTemplate = input<TemplateRef<any> | null>(null);\n queryActionsTemplate = input<TemplateRef<any> | null>(null);\n\n edited = output<void>();\n deleted = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition; item: PersistentObject }>();\n\n colors = Color;\n errorMessage = signal<string | null>(null);\n entityType = signal<EntityType | null>(null);\n\n /** The sub-queries rendered as cards below the form, in object form (#460, D17). */\n protected readonly subQueries = computed(() => subQueriesOf(this.entityType()));\n allEntityTypes = signal<EntityType[]>([]);\n item = signal<PersistentObject | null>(null);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n asDetailTypes = signal<Record<string, EntityType>>({});\n asDetailReferenceOptions = signal<Record<string, Record<string, QueryResultItem[]>>>({});\n type = '';\n id = '';\n /** The catalogue's Edit (#467, D8) when this page offers it: listed for the caller, on the detail side, and allowed on this row. */\n editAction = signal<CustomActionDefinition | null>(null);\n /** The catalogue's Delete, on the same terms as {@link editAction}. */\n deleteAction = signal<CustomActionDefinition | null>(null);\n canEdit = computed(() => this.editAction() !== null);\n canDelete = computed(() => this.deleteAction() !== null);\n customActions = signal<CustomActionDefinition[]>([]);\n /** Type-level rights, kept for the add-on context; null until loaded. */\n permissions = signal<EntityPermissions | null>(null);\n\n /**\n * The route's `?deleted=` mode (#460, T2): `only` when the row was opened from the recycle bin.\n * Sent with every load of this object; the server honours it only for `ViewDeleted` holders, so\n * for anyone else a deleted row stays a 404.\n */\n deletedMode = signal<SparkDeletedFilter | null>(null);\n\n /** True when the page shows a soft-deleted row: nothing but Restore/Purge applies to it. */\n protected readonly isDeletedView = computed(() => this.deletedMode() === 'only');\n\n /** What every add-on panel and action receives; rebuilt whenever the object or rights change. */\n protected readonly detailContext = computed((): SparkDetailContext | null => {\n const item = this.item();\n const entityType = this.entityType();\n if (!item || !entityType) return null;\n return {\n type: this.type,\n id: this.id,\n item,\n entityType,\n permissions: this.permissions(),\n deleted: this.deletedMode(),\n reload: () => this.load(),\n };\n });\n\n /**\n * The actions actually offered for the object on screen.\n *\n * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE -- the server is never told which\n * row is open -- so an action that applies to only some rows cannot be filtered there. The\n * entity's actions hook decides while it has the entity in hand and withholds what does not\n * apply, and the object arrives carrying that answer.\n *\n * Since #460 (D13) the server asks the same hook again when the action is submitted and refuses a\n * disabled one with 403, so hiding it here matches what the server will do. What this prevents is\n * offering a destructive action where it cannot possibly apply -- Coverage showed an irreversible\n * red \"Delete data\" button on every repository page, healthy ones included, and only admitted it\n * would refuse after the confirmation prompt.\n */\n visibleCustomActions = computed(() => {\n const withheld = this.item()?.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\n constructor() {\n // The query map too: Restore navigates from `?deleted=only` to the same path without it, and the\n // object must then be re-read as a live row.\n combineLatest([this.route.paramMap, this.route.queryParamMap])\n .pipe(takeUntilDestroyed())\n .subscribe(([params, query]) => this.onParamsChange(params, query.get('deleted')));\n\n // Server-issued `refreshAttribute` patches for the object this page is showing.\n //\n // Tracks the token and the entity type, never `item()`: writing the signal this effect reads\n // would re-run it forever, so the read-modify-write of the object happens untracked.\n effect(() => {\n const objectTypeId = this.entityType()?.id;\n if (!this.attributeRefresh.tokenFor(objectTypeId, this.id)) return;\n\n const patches = this.attributeRefresh.patchesFor(objectTypeId, this.id);\n untracked(() => this.applyAttributePatches(patches));\n });\n }\n\n /**\n * Applies patched attribute values in place, without a re-fetch.\n *\n * The operation carries the value the server computed after its own write, so this is both\n * cheaper and more correct than re-reading: a re-read goes through a RavenDB index that may\n * still be stale from the very write that produced the patch, and would show the old value.\n */\n private applyAttributePatches(patches: Record<string, SparkAttributePatch>): void {\n const current = this.item();\n if (!current) return;\n\n let changed = false;\n const attributes = current.attributes.map(attribute => {\n if (!Object.prototype.hasOwnProperty.call(patches, attribute.name)) return attribute;\n\n const patch = patches[attribute.name];\n const next = { ...attribute };\n let patched = false;\n\n // Each field is applied on its own, because they are not interchangeable: an AsDetail\n // attribute carries its rows in object/objects and leaves value null, so applying only\n // value could never refresh a detail grid.\n //\n // Compared by identity in every case. A scalar skips when unchanged, and a null skips\n // against a null -- which matters because the server writes object/objects as null on a\n // scalar patch, and treating that as a change would repaint on every patch. A rewritten\n // collection is a new array and so never matches, which is the behaviour wanted: comparing\n // rows deeply would cost more than the repaint it saves.\n if (patch.value !== undefined && next.value !== patch.value) {\n next.value = patch.value;\n patched = true;\n }\n // Only for the attributes that actually use them. The server writes object/objects as\n // null on every scalar patch, so applying them unconditionally would mark a scalar\n // attribute changed on each patch and repaint for nothing.\n if (attribute.dataType === 'AsDetail') {\n if (patch.object !== undefined && next.object !== patch.object) {\n next.object = patch.object as PersistentObject | null;\n patched = true;\n }\n if (patch.objects !== undefined && next.objects !== patch.objects) {\n next.objects = patch.objects as PersistentObject[] | null;\n patched = true;\n }\n }\n\n if (!patched) return attribute;\n changed = true;\n return next;\n });\n\n if (changed) this.item.set({ ...current, attributes });\n }\n\n private async onParamsChange(params: any, deleted: string | null = null): Promise<void> {\n this.type = params.get('type') || '';\n this.id = params.get('id') || '';\n const mode = parseSparkDeletedParam(deleted);\n // `exclude` is the default on the wire; keep it out of the request like an absent parameter.\n this.deletedMode.set(mode === 'exclude' ? null : mode);\n await this.load();\n }\n\n /** The object as the page's current mode sees it. Only a set mode is sent. */\n private loadItem(): Promise<PersistentObject> {\n const deleted = this.deletedMode();\n return deleted\n ? this.sparkService.get(this.type, this.id, { deleted })\n : this.sparkService.get(this.type, this.id);\n }\n\n /** (Re)loads the object, its type, rights and actions. Also what add-ons call via `context.reload()`. */\n async load(): Promise<void> {\n this.errorMessage.set(null);\n try {\n const [entityTypes, item] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.loadItem()\n ]);\n\n this.allEntityTypes.set(entityTypes);\n this.entityType.set(entityTypes.find(t => t.id === this.type || t.alias === this.type) || null);\n this.item.set(item);\n this.loadLookupReferenceOptions();\n this.loadAsDetailTypes();\n\n const et = this.entityType();\n if (et) {\n const [permissions, actions] = await Promise.all([\n this.sparkService.getPermissions(et.id),\n this.sparkService.getCustomActions(et.id)\n ]);\n // Prefer the per-row affordances (#236 G5) when the server attached them — a row the\n // caller may read but not mutate then hides its Edit/Delete buttons instead of rendering\n // ones that 404. `can` is absent for types with no row rule, so fall back to type-level.\n // The override (not an intersection) is safe because the server computes `can` as the\n // intersection of type-level rights and the row rule (#243) — it never claims more than\n // `permissions` does.\n const can = item.can;\n // The built-in actions honour `disabledActions` too (#460, D13): the server's\n // OnDisableActionsAsync decides, and refuses a disabled Edit/Save/Delete with 403 at submit,\n // so offering the button would only lead to that refusal. Case-insensitive, like the\n // custom-action filter below.\n const withheld = new Set((item.disabledActions ?? []).map(name => name.toLowerCase()));\n // A row opened from the recycle bin (#460): Edit and Delete judge live rows only — the\n // SoftDelete policy hides a deleted row from both — so they would 404. Restore/Purge come\n // from the soft-delete entry point's detail action instead.\n const deletedView = this.isDeletedView();\n this.permissions.set(permissions);\n // Edit and Delete come from the composed catalogue (#467, D8): listed only when the caller\n // holds Edit/T or Delete/T, shown here when their showedOn includes the detail side (an app\n // removes one with \"Edit\": null), and the row's `can` must allow it too. The selection rule\n // does not apply: the page acts on the one object it shows.\n const edit = builtInAction(actions, 'Edit');\n const del = builtInAction(actions, 'Delete');\n this.editAction.set(edit && isShowedOnDetail(edit.showedOn) && !deletedView\n && (can ? can.edit : permissions.canEdit) && !withheld.has('edit') && !withheld.has('save') ? edit : null);\n this.deleteAction.set(del && isShowedOnDetail(del.showedOn) && !deletedView\n && (can ? can.delete : permissions.canDelete) && !withheld.has('delete') ? del : null);\n // Custom actions only: the built-ins are rendered above as this page's Edit and Delete.\n this.customActions.set(deletedView ? [] : filterDetailActions(actions));\n }\n } catch (e) {\n const error = e as HttpErrorResponse;\n this.errorMessage.set(error.error?.error || error.message || 'An unexpected error occurred');\n }\n }\n\n /**\n * Drawn from the loaded object's own `showedOn`, falling back to the model's (#264, G-Q3): an action sets\n * it per object in `OnLoadAsync` — a stolen car's police report number is shown, the model says `None`.\n */\n visibleAttributes = computed(() => {\n const loaded = new Map((this.item()?.attributes ?? []).map(a => [a.name, a] as const));\n return this.entityType()?.attributes\n .filter(a => hasShowedOnFlag(loaded.get(a.name)?.showedOn ?? a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n private static readonly DEFAULT_TAB: AttributeTab = { id: '__default__', name: 'Algemeen', label: { nl: 'Algemeen', en: 'General' }, order: 0 };\n\n ungroupedAttributes = computed(() => {\n const attrs = this.visibleAttributes();\n const groupIds = new Set((this.entityType()?.groups || []).map(g => g.id));\n return attrs.filter(a => !a.group || !groupIds.has(a.group));\n });\n\n resolvedTabs = computed((): AttributeTab[] => {\n const et = this.entityType();\n const definedTabs = et?.tabs?.length ? [...et.tabs].sort((a, b) => a.order - b.order) : [];\n const hasUngroupedAttrs = this.ungroupedAttributes().length > 0;\n const hasUntabbedGroups = (et?.groups || []).some(g => !g.tab);\n\n if (hasUngroupedAttrs || hasUntabbedGroups || definedTabs.length === 0) {\n return [SparkPoDetailComponent.DEFAULT_TAB, ...definedTabs];\n }\n return definedTabs;\n });\n\n groupsForTab(tab: AttributeTab): AttributeGroup[] {\n const groups = this.entityType()?.groups || [];\n if (tab.id === '__default__') {\n return groups.filter(g => !g.tab).sort((a, b) => a.order - b.order);\n }\n return groups.filter(g => g.tab === tab.id).sort((a, b) => a.order - b.order);\n }\n\n attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[] {\n return this.visibleAttributes().filter(a => a.group === group.id);\n }\n\n /** Whether the attribute's renderer asks for the whole row, without a label (`fullWidth`). */\n isFullWidthRenderer(attr: EntityAttributeDefinition): boolean {\n if (!attr.renderer) return false;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.fullWidth === true;\n }\n\n getDetailRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.detailComponent ?? null;\n }\n\n /** Inputs of an AsDetail row renderer (`rowComponent`): the row and the object that owns it. */\n getRowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>, item: PersistentObject): Record<string, any> {\n return rowRendererInputs(renderer, row, item?.id);\n }\n\n getDetailRendererInputs(component: Type<any>, attr: EntityAttributeDefinition, item: PersistentObject): Record<string, any> {\n const itemAttr = item.attributes.find(a => a.name === attr.name);\n const formData: Record<string, any> = {};\n for (const a of item.attributes) {\n formData[a.name] = rendererValue(a);\n }\n return withDeclaredInputs(component, {\n value: rendererValue(itemAttr),\n attribute: attr,\n options: attr.rendererOptions,\n formData,\n item,\n });\n }\n\n /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */\n getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.columnComponent ?? null;\n }\n\n getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n });\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.visibleAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n // A lookup that fails to load only costs its labels: the attribute then shows its raw value\n // (attributeValue's fallback) instead of the whole page erroring over a secondary request (#453).\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const result = await this.sparkService.getLookupReference(name).catch(() => null);\n return [name, result] as const;\n })\n );\n this.lookupReferenceOptions.set(entries.reduce(\n (acc, [k, v]) => v ? { ...acc, [k]: v } : acc,\n {} as Record<string, LookupReference>));\n }\n\n private async loadAsDetailTypes(): Promise<void> {\n const asDetailAttrs = this.visibleAttributes().filter(a => a.dataType === 'AsDetail' && a.isArray && a.asDetailType);\n if (asDetailAttrs.length === 0) return;\n\n const types = this.allEntityTypes();\n const newAsDetailTypes: Record<string, EntityType> = {};\n // Same fallback as the form: a row type is usually absent from the Query-gated catalogue, and\n // the server now ships its definition alongside the parent (#385).\n const embedded = this.entityType()?.detailTypes ?? [];\n\n for (const attr of asDetailAttrs) {\n const asDetailType =\n types.find(t => t.clrType === attr.asDetailType)\n ?? embedded.find(t => t.clrType === attr.asDetailType);\n if (asDetailType) {\n newAsDetailTypes[attr.name] = asDetailType;\n const refCols = asDetailType.attributes.filter(a => a.dataType === 'Reference' && a.query);\n if (refCols.length > 0) {\n const refEntries = await Promise.all(\n refCols.map(async col => {\n const result = await this.sparkService.executeQueryByName(col.query!, {\n parentId: this.id,\n parentType: this.type,\n });\n return [col.name, result.items] as const;\n })\n );\n this.asDetailReferenceOptions.update(prev => ({\n ...prev,\n [attr.name]: refEntries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, QueryResultItem[]>)\n }));\n }\n }\n }\n this.asDetailTypes.set(newAsDetailTypes);\n }\n\n /**\n * The bootstrap classes for a custom action's button. Outline-primary stays the default, so\n * every existing action looks exactly as it did; a server-declared variant opts one action into\n * a louder style. Restricted to a known set rather than interpolated, because the value arrives\n * from a JSON file and must not be able to put arbitrary classes on the button.\n */\n /** Edit's and Delete's button classes: the catalogue's variant, else the look they always had. */\n protected builtInActionClass(action: CustomActionDefinition, fallback: 'primary' | 'danger'): string {\n const variant = action.variant?.toLowerCase();\n return ['danger', 'warning', 'primary', 'secondary', 'success'].includes(variant ?? '')\n ? `btn btn-${variant}`\n : `btn btn-${fallback}`;\n }\n\n protected customActionClass(action: CustomActionDefinition): string {\n const variant = action.variant?.toLowerCase();\n switch (variant) {\n case 'danger':\n case 'warning':\n case 'primary':\n case 'secondary':\n case 'success':\n return `btn btn-${variant}`;\n default:\n return 'btn btn-outline-primary';\n }\n }\n\n /**\n * The action currently running, or null. Drives the disabled state on every custom-action\n * button.\n *\n * A custom action is not necessarily quick: Coverage's Resync makes paged GitHub App calls for\n * every account the caller manages, inside the request. Without this the button stayed live and\n * looked inert for several seconds, and a second click queued a second full run -- which is how\n * a slow action and a broken one became indistinguishable.\n */\n runningAction = signal<string | null>(null);\n\n /**\n * Why an action cannot run in this browser, translated, for its tooltip: the client method it\n * requires (`requiresClient`) is not registered or not supported here (Q9). Null otherwise.\n */\n protected customActionUnavailableReason(action: CustomActionDefinition): string | null {\n const key = this.clientMethods.unavailableReason(action.requiresClient);\n return key === null ? null : this.lang.t(key);\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n // Guard re-entry as well as disabling the button: the template is not the only caller, and a\n // host component driving this method directly would otherwise bypass the check.\n if (this.runningAction()) return;\n if (this.clientMethods.unavailableReason(action.requiresClient) !== null) return;\n\n const message = confirmationText(action, 1);\n if (message && !confirm(message)) return;\n\n this.runningAction.set(action.name);\n try {\n await this.sparkService.executeCustomAction(this.type, action.name, this.item() || undefined);\n this.customActionExecuted.emit({ action, item: this.item()! });\n if (action.refreshOnCompleted) {\n // The sub-query grids below do not depend on item(), so re-fetching the PO left them\n // showing pre-action rows -- the action appeared to have done nothing to the very\n // lists it changed.\n //\n // Before the await, not after (#319): a server-issued refreshQuery was already dispatched\n // inside executeCustomAction, and bumps in one synchronous run coalesce into one grid\n // fetch. Bumping after the re-fetch's network round trip landed in a second\n // change-detection flush, and the grid fetched twice for one click.\n for (const subQuery of this.subQueries()) {\n this.queryRefresh.request(subQuery.query);\n }\n\n const item = await this.loadItem();\n this.item.set(item);\n }\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || 'Action failed');\n } finally {\n // finally, not after the try: an action that fails must not leave every button on the page\n // permanently disabled.\n this.runningAction.set(null);\n }\n }\n\n onEdit(): void {\n this.edited.emit();\n this.router.navigate(['/po', this.type, this.id, 'edit']);\n }\n\n async onDelete(): Promise<void> {\n // The version on screen is the one deleted (#467, D14): a row changed since is a 409.\n const etag = this.item()?.etag;\n if (!etag) return;\n const message = confirmationText(this.deleteAction(), 1);\n if (!message || confirm(message)) {\n await this.sparkService.delete(this.type, this.id, etag);\n this.deleted.emit();\n // Back to the list the row was opened from (else the type's list), not the start page.\n await this.returnNavigation.returnToList(this.entityType()?.name);\n }\n }\n\n onBack(): void {\n window.history.back();\n }\n}\n","<bs-container>\n<div class=\"container\">\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n } @else if (item(); as currentItem) {\n @if (entityType(); as et) {\n <div class=\"spark-actionbar py-2\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-outline-secondary\" (click)=\"onBack()\">\n <spark-icon name=\"arrow-left\" /> {{ 'common.back' | t }}\n </button>\n <!-- Edit and Delete are the composed catalogue's (#467, D8): label, icon and variant included. -->\n @if (editAction(); as edit) {\n <button *bsPriorityNavItem=\"2\" [class]=\"builtInActionClass(edit, 'primary')\" (click)=\"onEdit()\">\n @if (edit.icon) { <spark-icon [name]=\"edit.icon\" /> } {{ edit.label | resolveTranslation }}\n </button>\n }\n @if (deleteAction(); as del) {\n <button *bsPriorityNavItem=\"3\" [class]=\"builtInActionClass(del, 'danger')\" (click)=\"onDelete()\">\n @if (del.icon) { <spark-icon [name]=\"del.icon\" /> } {{ del.label | resolveTranslation }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of visibleCustomActions(); track action.name) {\n @let unavailable = customActionUnavailableReason(action);\n <button *bsPriorityNavItem=\"10 + action.offset\" [class]=\"customActionClass(action)\" [attr.data-action]=\"action.name\"\n [disabled]=\"runningAction() !== null || unavailable !== null\" [attr.title]=\"unavailable\" (click)=\"onCustomAction(action)\">\n {{ action.label | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n <!-- Add-on actions (SPARK_DETAIL_ACTIONS): each decides for itself whether it applies. -->\n @if (detailContext(); as ctx) {\n @for (action of detailActions; track action.id) {\n <ng-container *bsPriorityNavItem=\"action.priority ?? 60\">\n <ng-container *ngComponentOutlet=\"action.component; inputs: { context: ctx }\" />\n </ng-container>\n }\n }\n </bs-priority-nav>\n </div>\n <h2>\n {{ currentItem.breadcrumb || currentItem.name }}\n @if (isDeletedView()) {\n <bs-badge [type]=\"colors.secondary\" class=\"align-middle ms-2 spark-deleted-badge\">{{ 'softDelete.deleted' | t }}</bs-badge>\n }\n </h2>\n\n <bs-grid>\n <bs-tab-control>\n @for (tab of resolvedTabs(); track tab.id) {\n <bs-tab-page>\n <ng-template bsTabPageHeader>{{ tab.label | resolveTranslation:tab.name }}</ng-template>\n <ng-container *ngTemplateOutlet=\"detailTabContent; context: { $implicit: tab }\"></ng-container>\n </bs-tab-page>\n }\n </bs-tab-control>\n\n <ng-template #detailTabContent let-tab>\n @if (tab.id === '__default__') {\n @let ungroupedAttrs = ungroupedAttributes();\n @if (ungroupedAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n <div class=\"p-3\">\n <dl bsRow>\n @for (attr of ungroupedAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"detailAttrField; context: { $implicit: attr, item: currentItem }\"></ng-container>\n }\n </dl>\n </div>\n </bs-card>\n }\n }\n @for (group of groupsForTab(tab); track group.id) {\n @if (attrsForGroup(group); as groupAttrs) {\n @if (groupAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n @if (group.label) {\n <bs-card-header>{{ group.label | resolveTranslation:group.name }}</bs-card-header>\n }\n <div class=\"p-3\">\n <dl bsRow>\n @for (attr of groupAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"detailAttrField; context: { $implicit: attr, item: currentItem }\"></ng-container>\n }\n </dl>\n </div>\n </bs-card>\n }\n }\n }\n </ng-template>\n\n <ng-template #detailAttrField let-attr let-currentItem=\"item\">\n <!-- A full-width renderer (the core 'paragraph') is body text: no label, the whole row. -->\n @let fullWidth = isFullWidthRenderer(attr);\n @if (!fullWidth) {\n <dt [sm]=\"3\">{{ (attr.label | resolveTranslation) || attr.name }}<spark-attribute-description [description]=\"attr.description\" /></dt>\n }\n <dd [sm]=\"fullWidth ? 12 : 9\">\n @if (getDetailRendererComponent(attr); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: getDetailRendererInputs(rendererType, attr, currentItem)\"></ng-container>\n } @else if (attr.dataType === 'AsDetail' && attr.isArray) {\n <bs-table [isResponsive]=\"true\">\n <thead>\n <tr>\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ (col.label | resolveTranslation) || col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n </tr>\n </thead>\n <tbody class=\"align-middle\">\n @let rowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n @for (row of (attr.name | arrayValue:currentItem); track $index) {\n <tr>\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <td>\n <!--\n Shared with the query grid. This cell used to hand-roll the renderer\n dispatch and print everything else as text, so a `boolean` column read\n \"true\" here and rendered a checkbox in a grid, and a `color` showed its\n hex. Only the VALUE is resolved locally: these rows are plain\n dictionaries, not PersistentObjects.\n\n `chips` is left unset for a reference array on purpose — an AsDetail\n row's `__sparkBreadcrumbs` is keyed by column, not by id, so there are\n no labels for the members. Text beats a row of raw ids.\n -->\n <spark-grid-cell\n [column]=\"col\"\n [display]=\"(row | asDetailCellValue:attr:col:asDetailReferenceOptions())\"\n [rendererValue]=\"row[col.name]\"\n [item]=\"row\"\n [link]=\"col.dataType === 'Reference' && col.referenceType\n ? (col.referenceType | referenceLinkRoute:row[col.name]:allEntityTypes())\n : null\" />\n <!-- A row renderer (rowComponent: contributions' attribution line) draws\n its attributes once, under the row's first cell, instead of as columns. -->\n @if (firstCol && rowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"rowRenderer.component; inputs: getRowRendererInputs(rowRenderer, row, currentItem)\"></ng-container>\n </div>\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n } @else if (attr.dataType === 'boolean') {\n <input type=\"checkbox\"\n [checked]=\"(attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) === true\"\n [indeterminate]=\"(attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) == null\"\n disabled\n onclick=\"return false;\"\n class=\"opacity-100\">\n } @else if (attr.dataType === 'color') {\n @let colorVal = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (colorVal) {\n <span class=\"spark-color-swatch d-inline-block align-middle border rounded me-2\" [style.background-color]=\"colorVal\"></span>\n {{ colorVal }}\n } @else {\n -\n }\n } @else if (attr.dataType === 'image') {\n @let imageSrc = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (imageSrc) {\n <img [src]=\"imageSrc\" [alt]=\"(attr.label | resolveTranslation) || attr.name\" loading=\"lazy\"\n class=\"img-fluid rounded\" style=\"max-height: 12rem;\">\n } @else {\n -\n }\n } @else if (attr.dataType === 'url') {\n @let hrefVal = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (hrefVal) {\n <a [href]=\"hrefVal\" target=\"_blank\" rel=\"noopener noreferrer\">{{ hrefVal }}</a>\n } @else {\n -\n }\n } @else if (attr.dataType === 'Reference' && attr.isArray) {\n @let chips = (attr.name | referenceChips:item());\n @if (chips.length) {\n <div class=\"d-flex flex-wrap gap-1\">\n @for (chip of chips; track chip.id) {\n @let chipRoute = (attr.referenceType ? (attr.referenceType | referenceLinkRoute:chip.id:allEntityTypes()) : null);\n @if (chipRoute) {\n <a class=\"spark-chip\" [routerLink]=\"chipRoute\">{{ chip.label }}</a>\n } @else {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n }\n </div>\n } @else {\n -\n }\n } @else if (attr.dataType === 'Reference' && attr.referenceType) {\n @let refRoute = (attr.referenceType | referenceLinkRoute:(attr.name | rawAttributeValue:item()):allEntityTypes());\n @if (refRoute && (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes())) {\n <a [routerLink]=\"refRoute\">{{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) }}</a>\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n } @else if (attr.dataType === 'MultiLineString') {\n <div class=\"spark-multiline-value\">{{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}</div>\n } @else if (attr.dataType === 'date' || attr.dataType === 'datetime') {\n <!--\n Formatted the same way, and in the same timezone, as the grid renders it.\n\n Before this, a detail page printed the raw wire value — `2026-12-31T23:59:00-08:00`\n in a definition list — while the grid showed `01/01/2027, 08:59` for the same\n document. Not a rounding difference: a DateTimeOffset whose offset differs from the\n viewer's can disagree on the DATE, so the two pages named different days for one car.\n\n The grid's meaning wins because it is the one a reader can act on: a timestamp is an\n instant, shown in the viewer's own zone. The originating offset is still on the wire\n for code that needs it — server-side logic, exports, \"which country's morning was\n this?\" — it simply is not what a viewer is shown by default.\n -->\n @let when = (attr.name | rawAttributeValue:item()) | parsedDate;\n @if (when) {\n {{ when | date:(attr.dataType === 'date' ? 'mediumDate' : 'short') }}\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n </dd>\n </ng-template>\n </bs-grid>\n @for (subQuery of subQueries(); track subQuery.query) {\n <!-- The entry's selectionMode overrides the query's own for this parent (#460, D17). -->\n <spark-query-card [queryId]=\"subQuery.query\" [parentId]=\"currentItem.id!\" [parentType]=\"et.name\"\n [selectionMode]=\"subQuery.selectionMode ?? null\"\n [parentDeleted]=\"isDeletedView()\"\n [iconTemplate]=\"queryIconTemplate()\" [captionTemplate]=\"queryCaptionTemplate()\"\n [actionsTemplate]=\"queryActionsTemplate()\" />\n }\n @if (extraContentTemplate(); as extraContentTpl) {\n <ng-container *ngTemplateOutlet=\"extraContentTpl; context: { $implicit: currentItem, entityType: et }\"></ng-container>\n }\n <!-- Add-on panels (SPARK_DETAIL_PANELS), e.g. <spark-po-history>. -->\n @if (detailContext(); as ctx) {\n @for (panel of detailPanels; track panel.id) {\n <div class=\"spark-detail-panel m-3\">\n <ng-container *ngComponentOutlet=\"panel.component; inputs: { context: ctx }\" />\n </div>\n }\n }\n }\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n</bs-container>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,IAAa,yBAAb,MAAa,uBAAsB;CAChB,QAAQ,OAAO,cAAc;CAC7B,SAAS,OAAO,MAAM;CACtB,eAAe,OAAO,YAAY;CAClC,mBAAmB,OAAO,4BAA4B;CACtD,eAAe,OAAO,wBAAwB;CAC9C,mBAAmB,OAAO,4BAA4B;CACpD,OAAO,OAAO,oBAAoB;CACpC,gBAAgB,OAAO,yBAAyB;CAC9C,mBAAmB,OAAO,yBAAyB;CAOnD,eAAe,qBAAqB,OAAO,qBAAqB,EAAE,UAAU,KAAI,CAAE,IAAG,MAAK,EAAE,KAAK;CACjG,gBAAgB,qBAAqB,OAAO,sBAAsB,EAAE,UAAU,KAAI,CAAE,IAAG,MAAK,EAAE,YAAY,EAAE;CAE/H,oBAAoB,MAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAC/B,uBAAuB,MAAgC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAC5D,uBAAuB,MAAmF,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAc/G,oBAAoB,MAA+B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CACxD,uBAAuB,MAA+B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAC3D,uBAAuB,MAA+B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAE3D,SAAS,OAAM;CACf,UAAU,OAAM;CAChB,uBAAuB,OAAM;CAE7B,SAAS;CACT,eAAe,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAC1C,aAAa,OAA0B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,aAAa,eAAe,aAAa,KAAK,WAAU,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAC/E,iBAAiB,OAAqB,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CACzC,OAAO,OAAgC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAC5C,yBAAyB,OAAwC,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,yBAAA,CAAA,+BAAA,CAAA,CAAA;CACpE,gBAAgB,OAAmC,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CACtD,2BAA2B,OAA0D,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,2BAAA,CAAA,+BAAA,CAAA,CAAA;CACxF,OAAO;CACP,KAAK;CAEL,aAAa,OAAsC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAExD,eAAe,OAAsC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAC1D,UAAU,eAAe,KAAK,WAAU,MAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CACpD,YAAY,eAAe,KAAK,aAAY,MAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CACxD,gBAAgB,OAAiC,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAEpD,cAAc,OAAiC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAOpD,cAAc,OAAkC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGlC,gBAAgB,eAAe,KAAK,YAAW,MAAO,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAG7D,gBAAgB,eAAyC;EAC1E,MAAM,OAAO,KAAK,KAAI;EACtB,MAAM,aAAa,KAAK,WAAU;EAClC,IAAI,CAAC,QAAQ,CAAC,YAAY,OAAO;EACjC,OAAO;GACL,MAAM,KAAK;GACX,IAAI,KAAK;GACT;GACA;GACA,aAAa,KAAK,YAAW;GAC7B,SAAS,KAAK,YAAW;GACzB,cAAc,KAAK,KAAI;EACxB;CACH,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAgBF,uBAAuB,eAAc;EACnC,MAAM,WAAW,KAAK,KAAI,CAAE,EAAE;EAC9B,IAAI,CAAC,UAAU,QAAQ,OAAO,KAAK,cAAa;EAEhD,MAAM,UAAU,IAAI,IAAI,SAAS,KAAI,SAAQ,KAAK,YAAW,CAAE,CAAC;EAChE,OAAO,KAAK,cAAa,CAAE,CAAC,QAAO,WAAU,CAAC,QAAQ,IAAI,OAAO,KAAK,YAAW,CAAE,CAAC;CACtF,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EAGE,cAAc,CAAC,KAAK,MAAM,UAAU,KAAK,MAAM,aAAa,CAAC,CAAC,CAC3D,KAAK,mBAAkB,CAAE,CAAC,CAC1B,WAAW,CAAC,QAAQ,WAAW,KAAK,eAAe,QAAQ,MAAM,IAAI,SAAS,CAAC,CAAC;EAMnF,aAAY;GACV,MAAM,eAAe,KAAK,WAAU,CAAE,EAAE;GACxC,IAAI,CAAC,KAAK,iBAAiB,SAAS,cAAc,KAAK,EAAE,GAAG;GAE5D,MAAM,UAAU,KAAK,iBAAiB,WAAW,cAAc,KAAK,EAAE;GACtE,gBAAgB,KAAK,sBAAsB,OAAO,CAAC;EACrD,CAAC;CACH;CASQ,sBAAsB,SAA4C;EACxE,MAAM,UAAU,KAAK,KAAI;EACzB,IAAI,CAAC,SAAS;EAEd,IAAI,UAAU;EACd,MAAM,aAAa,QAAQ,WAAW,KAAI,cAAY;GACpD,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,UAAU,IAAI,GAAG,OAAO;GAE3E,MAAM,QAAQ,QAAQ,UAAU;GAChC,MAAM,OAAO,EAAE,GAAG,UAAS;GAC3B,IAAI,UAAU;GAWd,IAAI,MAAM,UAAU,UAAa,KAAK,UAAU,MAAM,OAAO;IAC3D,KAAK,QAAQ,MAAM;IACnB,UAAU;GACZ;GAIA,IAAI,UAAU,aAAa,YAAY;IACrC,IAAI,MAAM,WAAW,UAAa,KAAK,WAAW,MAAM,QAAQ;KAC9D,KAAK,SAAS,MAAM;KACpB,UAAU;IACZ;IACA,IAAI,MAAM,YAAY,UAAa,KAAK,YAAY,MAAM,SAAS;KACjE,KAAK,UAAU,MAAM;KACrB,UAAU;IACZ;GACF;GAEA,IAAI,CAAC,SAAS,OAAO;GACrB,UAAU;GACV,OAAO;EACT,CAAC;EAED,IAAI,SAAS,KAAK,KAAK,IAAI;GAAE,GAAG;GAAS;EAAU,CAAE;CACvD;CAEQ,MAAM,eAAe,QAAa,UAAyB,MAAI;EACrE,KAAK,OAAO,OAAO,IAAI,MAAM,KAAK;EAClC,KAAK,KAAK,OAAO,IAAI,IAAI,KAAK;EAC9B,MAAM,OAAO,uBAAuB,OAAO;EAE3C,KAAK,YAAY,IAAI,SAAS,YAAY,OAAO,IAAI;EACrD,MAAM,KAAK,KAAI;CACjB;CAGQ,WAAQ;EACd,MAAM,UAAU,KAAK,YAAW;EAChC,OAAO,UACH,KAAK,aAAa,IAAI,KAAK,MAAM,KAAK,IAAI,EAAE,QAAO,CAAE,IACrD,KAAK,aAAa,IAAI,KAAK,MAAM,KAAK,EAAE;CAC9C;CAGA,MAAM,OAAI;EACR,KAAK,aAAa,IAAI,IAAI;EAC1B,IAAI;GACF,MAAM,CAAC,aAAa,QAAQ,MAAM,QAAQ,IAAI,CAC5C,KAAK,aAAa,eAAc,GAChC,KAAK,SAAQ,CACd,CAAA;GAED,KAAK,eAAe,IAAI,WAAW;GACnC,KAAK,WAAW,IAAI,YAAY,MAAK,MAAK,EAAE,OAAO,KAAK,QAAQ,EAAE,UAAU,KAAK,IAAI,KAAK,IAAI;GAC9F,KAAK,KAAK,IAAI,IAAI;GAClB,KAAK,2BAA0B;GAC/B,KAAK,kBAAiB;GAEtB,MAAM,KAAK,KAAK,WAAU;GAC1B,IAAI,IAAI;IACN,MAAM,CAAC,aAAa,WAAW,MAAM,QAAQ,IAAI,CAC/C,KAAK,aAAa,eAAe,GAAG,EAAE,GACtC,KAAK,aAAa,iBAAiB,GAAG,EAAE,CACzC,CAAA;IAOD,MAAM,MAAM,KAAK;IAKjB,MAAM,WAAW,IAAI,KAAK,KAAK,mBAAmB,CAAA,EAAA,CAAI,KAAI,SAAQ,KAAK,YAAW,CAAE,CAAC;IAIrF,MAAM,cAAc,KAAK,cAAa;IACtC,KAAK,YAAY,IAAI,WAAW;IAKhC,MAAM,OAAO,cAAc,SAAS,MAAM;IAC1C,MAAM,MAAM,cAAc,SAAS,QAAQ;IAC3C,KAAK,WAAW,IAAI,QAAQ,iBAAiB,KAAK,QAAQ,KAAK,CAAC,gBAC1D,MAAM,IAAI,OAAO,YAAY,YAAY,CAAC,SAAS,IAAI,MAAM,KAAK,CAAC,SAAS,IAAI,MAAM,IAAI,OAAO,IAAI;IAC3G,KAAK,aAAa,IAAI,OAAO,iBAAiB,IAAI,QAAQ,KAAK,CAAC,gBAC1D,MAAM,IAAI,SAAS,YAAY,cAAc,CAAC,SAAS,IAAI,QAAQ,IAAI,MAAM,IAAI;IAEvF,KAAK,cAAc,IAAI,cAAc,CAAA,IAAK,oBAAoB,OAAO,CAAC;GACxE;EACF,SAAS,GAAG;GACV,MAAM,QAAQ;GACd,KAAK,aAAa,IAAI,MAAM,OAAO,SAAS,MAAM,WAAW,8BAA8B;EAC7F;CACF;CAMA,oBAAoB,eAAc;EAChC,MAAM,SAAS,IAAI,KAAK,KAAK,KAAI,CAAE,EAAE,cAAc,CAAA,EAAA,CAAI,KAAI,MAAK,CAAC,EAAE,MAAM,CAAC,CAAU,CAAC;EACrF,OAAO,KAAK,WAAU,CAAE,EAAE,WACvB,QAAO,MAAK,gBAAgB,OAAO,IAAI,EAAE,IAAI,CAAC,EAAE,YAAY,EAAE,UAAU,SAAS,gBAAgB,CAAC,CAAC,CACnG,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,KAAK,CAAA;CAC1C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAEM,OAAgB,cAA4B;EAAE,IAAI;EAAe,MAAM;EAAY,OAAO;GAAE,IAAI;GAAY,IAAI;EAAS;EAAI,OAAO;CAAC;CAE7I,sBAAsB,eAAc;EAClC,MAAM,QAAQ,KAAK,kBAAiB;EACpC,MAAM,WAAW,IAAI,KAAK,KAAK,WAAU,CAAE,EAAE,UAAU,CAAA,EAAA,CAAI,KAAI,MAAK,EAAE,EAAE,CAAC;EACzE,OAAO,MAAM,QAAO,MAAK,CAAC,EAAE,SAAS,CAAC,SAAS,IAAI,EAAE,KAAK,CAAC;CAC7D,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,sBAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,eAAe,eAA8B;EAC3C,MAAM,KAAK,KAAK,WAAU;EAC1B,MAAM,cAAc,IAAI,MAAM,SAAS,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;EACxF,MAAM,oBAAoB,KAAK,oBAAmB,CAAE,CAAC,SAAS;EAC9D,MAAM,qBAAqB,IAAI,UAAU,CAAA,EAAA,CAAI,MAAK,MAAK,CAAC,EAAE,GAAG;EAE7D,IAAI,qBAAqB,qBAAqB,YAAY,WAAW,GACnE,OAAO,CAAC,uBAAuB,aAAa,GAAG,WAAW;EAE5D,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,aAAa,KAAiB;EAC5B,MAAM,SAAS,KAAK,WAAU,CAAE,EAAE,UAAU,CAAA;EAC5C,IAAI,IAAI,OAAO,eACb,OAAO,OAAO,QAAO,MAAK,CAAC,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;EAEpE,OAAO,OAAO,QAAO,MAAK,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CAC9E;CAEA,cAAc,OAAqB;EACjC,OAAO,KAAK,kBAAiB,CAAE,CAAC,QAAO,MAAK,EAAE,UAAU,MAAM,EAAE;CAClE;CAGA,oBAAoB,MAA+B;EACjD,IAAI,CAAC,KAAK,UAAU,OAAO;EAC3B,OAAO,KAAK,iBAAiB,MAAK,MAAK,EAAE,SAAS,KAAK,QAAQ,CAAC,EAAE,cAAc;CAClF;CAEA,2BAA2B,MAA+B;EACxD,IAAI,CAAC,KAAK,UAAU,OAAO;EAC3B,OAAO,KAAK,iBAAiB,MAAK,MAAK,EAAE,SAAS,KAAK,QAAQ,CAAC,EAAE,mBAAmB;CACvF;CAGA,qBAAqB,UAAoC,KAA0B,MAAsB;EACvG,OAAO,kBAAkB,UAAU,KAAK,MAAM,EAAE;CAClD;CAEA,wBAAwB,WAAsB,MAAiC,MAAsB;EACnG,MAAM,WAAW,KAAK,WAAW,MAAK,MAAK,EAAE,SAAS,KAAK,IAAI;EAC/D,MAAM,WAAgC,CAAA;EACtC,KAAK,MAAM,KAAK,KAAK,YACnB,SAAS,EAAE,QAAQ,cAAc,CAAC;EAEpC,OAAO,mBAAmB,WAAW;GACnC,OAAO,cAAc,QAAQ;GAC7B,WAAW;GACX,SAAS,KAAK;GACd;GACA;EACD,CAAA;CACH;CAGA,iCAAiC,KAA8B;EAC7D,IAAI,CAAC,IAAI,UAAU,OAAO;EAC1B,OAAO,KAAK,iBAAiB,MAAK,MAAK,EAAE,SAAS,IAAI,QAAQ,CAAC,EAAE,mBAAmB;CACtF;CAEA,8BAA8B,WAAsB,KAA0B,KAA8B;EAC1G,OAAO,mBAAmB,WAAW;GACnC,OAAO,IAAI,IAAI;GACf,WAAW;GACX,SAAS,IAAI;GACb,MAAM;EACP,CAAA;CACH;CAEQ,MAAM,6BAA0B;EACtC,MAAM,cAAc,KAAK,kBAAiB,CAAE,CAAC,QAAO,MAAK,EAAE,mBAAmB;EAC9E,IAAI,YAAY,WAAW,GAAG;EAE9B,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,YAAY,KAAI,MAAK,EAAE,mBAAoB,CAAC,CAAC;EAG7E,MAAM,UAAU,MAAM,QAAQ,IAC5B,YAAY,IAAI,OAAM,SAAO;GAE3B,OAAO,CAAC,MAAM,MADO,KAAK,aAAa,mBAAmB,IAAI,CAAC,CAAC,YAAY,IAAI,CAC5D;EACtB,CAAC,CAAC;EAEJ,KAAK,uBAAuB,IAAI,QAAQ,QACrC,KAAK,CAAC,GAAG,OAAO,IAAI;GAAE,GAAG;IAAM,IAAI;EAAC,IAAK,KAC1C,CAAA,CAAqC,CAAC;CAC1C;CAEQ,MAAM,oBAAiB;EAC7B,MAAM,gBAAgB,KAAK,kBAAiB,CAAE,CAAC,QAAO,MAAK,EAAE,aAAa,cAAc,EAAE,WAAW,EAAE,YAAY;EACnH,IAAI,cAAc,WAAW,GAAG;EAEhC,MAAM,QAAQ,KAAK,eAAc;EACjC,MAAM,mBAA+C,CAAA;EAGrD,MAAM,WAAW,KAAK,WAAU,CAAE,EAAE,eAAe,CAAA;EAEnD,KAAK,MAAM,QAAQ,eAAe;GAChC,MAAM,eACJ,MAAM,MAAK,MAAK,EAAE,YAAY,KAAK,YAAY,KAC5C,SAAS,MAAK,MAAK,EAAE,YAAY,KAAK,YAAY;GACvD,IAAI,cAAc;IAChB,iBAAiB,KAAK,QAAQ;IAC9B,MAAM,UAAU,aAAa,WAAW,QAAO,MAAK,EAAE,aAAa,eAAe,EAAE,KAAK;IACzF,IAAI,QAAQ,SAAS,GAAG;KACtB,MAAM,aAAa,MAAM,QAAQ,IAC/B,QAAQ,IAAI,OAAM,QAAM;MACtB,MAAM,SAAS,MAAM,KAAK,aAAa,mBAAmB,IAAI,OAAQ;OACpE,UAAU,KAAK;OACf,YAAY,KAAK;MAClB,CAAA;MACD,OAAO,CAAC,IAAI,MAAM,OAAO,KAAK;KAChC,CAAC,CAAC;KAEJ,KAAK,yBAAyB,QAAO,UAAS;MAC5C,GAAG;OACF,KAAK,OAAO,WAAW,QAAQ,KAAK,CAAC,GAAG,QAAQ;OAAE,GAAG;QAAM,IAAI;MAAC,IAAK,CAAA,CAAuC;KAC9G,EAAC;IACJ;GACF;EACF;EACA,KAAK,cAAc,IAAI,gBAAgB;CACzC;CASU,mBAAmB,QAAgC,UAA8B;EACzF,MAAM,UAAU,OAAO,SAAS,YAAW;EAC3C,OAAO;GAAC;GAAU;GAAW;GAAW;GAAa;EAAS,CAAC,CAAC,SAAS,WAAW,EAAE,IAClF,WAAW,YACX,WAAW;CACjB;CAEU,kBAAkB,QAA8B;EACxD,MAAM,UAAU,OAAO,SAAS,YAAW;EAC3C,QAAQ,SAAR;GACE,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,WACH,OAAO,WAAW;GACpB,SACE,OAAO;EACX;CACF;CAWA,gBAAgB,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAMjC,8BAA8B,QAA8B;EACpE,MAAM,MAAM,KAAK,cAAc,kBAAkB,OAAO,cAAc;EACtE,OAAO,QAAQ,OAAO,OAAO,KAAK,KAAK,EAAE,GAAG;CAC9C;CAEA,MAAM,eAAe,QAA8B;EAGjD,IAAI,KAAK,cAAa,GAAI;EAC1B,IAAI,KAAK,cAAc,kBAAkB,OAAO,cAAc,MAAM,MAAM;EAE1E,MAAM,UAAU,iBAAiB,QAAQ,CAAC;EAC1C,IAAI,WAAW,CAAC,QAAQ,OAAO,GAAG;EAElC,KAAK,cAAc,IAAI,OAAO,IAAI;EAClC,IAAI;GACF,MAAM,KAAK,aAAa,oBAAoB,KAAK,MAAM,OAAO,MAAM,KAAK,KAAI,KAAM,MAAS;GAC5F,KAAK,qBAAqB,KAAK;IAAE;IAAQ,MAAM,KAAK,KAAI;GAAG,CAAE;GAC7D,IAAI,OAAO,oBAAoB;IAS7B,KAAK,MAAM,YAAY,KAAK,WAAU,GACpC,KAAK,aAAa,QAAQ,SAAS,KAAK;IAG1C,MAAM,OAAO,MAAM,KAAK,SAAQ;IAChC,KAAK,KAAK,IAAI,IAAI;GACpB;EACF,SAAS,GAAG;GACV,MAAM,MAAM;GACZ,KAAK,aAAa,IAAI,IAAI,OAAO,SAAS,IAAI,WAAW,eAAe;EAC1E,UAAU;GAGR,KAAK,cAAc,IAAI,IAAI;EAC7B;CACF;CAEA,SAAM;EACJ,KAAK,OAAO,KAAI;EAChB,KAAK,OAAO,SAAS;GAAC;GAAO,KAAK;GAAM,KAAK;GAAI;EAAM,CAAC;CAC1D;CAEA,MAAM,WAAQ;EAEZ,MAAM,OAAO,KAAK,KAAI,CAAE,EAAE;EAC1B,IAAI,CAAC,MAAM;EACX,MAAM,UAAU,iBAAiB,KAAK,aAAY,GAAI,CAAC;EACvD,IAAI,CAAC,WAAW,QAAQ,OAAO,GAAG;GAChC,MAAM,KAAK,aAAa,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI;GACvD,KAAK,QAAQ,KAAI;GAEjB,MAAM,KAAK,iBAAiB,aAAa,KAAK,WAAU,CAAE,EAAE,IAAI;EAClE;CACF;CAEA,SAAM;EACJ,OAAO,QAAQ,KAAI;CACrB;CAjgBW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,sBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,sBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,sBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,sBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,QAAA;GAAA,SAAA;GAAA,sBAAA;EAAA;EAAA,UAAA;EAAA,UCrEnC;EA8QA,QAAA,CAAA,0kCAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MD9MY;GAAY;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,mBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAuC;GAAY;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,mBAAA,qBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAC;IAAe,UAAA;IAAA,QAAA,CAAA,SAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAqB,UAAA;IAAA,QAAA,CAAA,SAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAoB,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAe,UAAA;IAAA,QAAA,CAAA,iBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAqB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAsB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,gBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAA0B,UAAA;IAAA,QAAA,CAAA,qBAAA,4BAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAqB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,UAAA;IAAA,QAAA,CAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAwB,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,UAAA;IAAA,QAAA,CAAA,QAAA,OAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,UAAA;IAAA,QAAA,CAAA,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAuB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAsB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAwO;IAAkC,UAAA;IAAA,QAAA,CAAA,eAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAxQ;IAAsB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAqB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAmB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAuB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAqB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAc,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAsB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAkB,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAc,MAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKrqB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EAPlC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAiB,SAClB;IAAC;IAAc;IAAkB;IAAmB;IAAc;IAAkB;IAAiB;IAAiB;IAAuB;IAAsB;IAAiB;IAAoB;IAAuB;IAAwB;IAA4B;IAAkB;IAAuB;IAAoB;IAA0B;IAAoB;IAAoB;IAAyB;IAAwB;IAAwB;IAAkB;IAAoB;IAAuB;IAAqB;IAAyB;IAAuB;IAAgB;IAAwB;IAAoB;IAAgB;GAAkC;GAAC,iBAGpsB,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,0kCAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"mintplayer-ng-spark-po-detail.mjs","names":[],"sources":["../../po-detail/src/spark-po-detail.component.ts","../../po-detail/src/spark-po-detail.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal, TemplateRef, Type, untracked } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CommonModule, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';\nimport { ActivatedRoute, Router, RouterModule } from '@angular/router';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsContainerComponent } from '@mintplayer/ng-bootstrap/container';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsPriorityNavComponent, BsPriorityNavItemDirective } from '@mintplayer/ng-bootstrap/priority-nav';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective } from '@mintplayer/ng-bootstrap/tab-control';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SparkService, SparkLanguageService, SparkReturnNavigationService } from '@mintplayer/ng-spark/services';\nimport { SparkAttributePatch, SparkAttributeRefreshService, SparkClientMethodRegistry, SparkQueryRefreshService } from '@mintplayer/ng-spark/client-operations';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n AttributeValuePipe,\n RawAttributeValuePipe,\n AsDetailColumnsPipe,\n AsDetailCellValuePipe,\n ArrayValuePipe,\n ReferenceLinkRoutePipe,\n ReferenceChipsPipe,\n ParsedDatePipe,\n AsDetailRowRendererPipe,\n} from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkQueryCardComponent, SparkGridCellComponent } from '@mintplayer/ng-spark/grid';\nimport { SPARK_ATTRIBUTE_RENDERERS, SparkResolvedRowRenderer, rendererValue, rowRendererInputs, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport {\n AttributeGroup,\n AttributeTab,\n CustomActionDefinition,\n EntityAttributeDefinition,\n EntityType,\n LookupReference,\n PersistentObject,\n QueryResultItem,\n ShowedOn,\n hasShowedOnFlag,\n EntityPermissions,\n SparkDeletedFilter,\n builtInAction,\n confirmationText,\n filterDetailActions,\n isShowedOnDetail,\n subQueriesOf,\n} from '@mintplayer/ng-spark/models';\nimport {\n SPARK_DETAIL_ACTIONS,\n SPARK_DETAIL_PANELS,\n SparkDetailContext,\n orderSparkExtensions,\n parseSparkDeletedParam,\n} from '@mintplayer/ng-spark/panels';\nimport { combineLatest } from 'rxjs';\n\n@Component({\n selector: 'spark-po-detail',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, RouterModule, BsAlertComponent, BsBadgeComponent,BsCardComponent, BsCardHeaderComponent, BsContainerComponent, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsPriorityNavComponent, BsPriorityNavItemDirective, BsTableComponent, BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective, BsSpinnerComponent, SparkIconComponent, SparkQueryCardComponent, SparkGridCellComponent, ResolveTranslationPipe, TranslateKeyPipe, AttributeValuePipe, RawAttributeValuePipe, AsDetailColumnsPipe, AsDetailRowRendererPipe, AsDetailCellValuePipe, ArrayValuePipe, ReferenceLinkRoutePipe, ReferenceChipsPipe, ParsedDatePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-po-detail.component.html',\n styleUrl: './spark-po-detail.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkPoDetailComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly sparkService = inject(SparkService);\n private readonly returnNavigation = inject(SparkReturnNavigationService);\n private readonly queryRefresh = inject(SparkQueryRefreshService);\n private readonly attributeRefresh = inject(SparkAttributeRefreshService);\n protected readonly lang = inject(SparkLanguageService);\n private readonly clientMethods = inject(SparkClientMethodRegistry);\n protected readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n\n /**\n * Add-on panels and action-bar buttons (#460, `SPARK_DETAIL_PANELS` / `SPARK_DETAIL_ACTIONS`).\n * Multi-provided, so History, SoftDelete and Moderation can each add theirs to the page\n * `sparkRoutes()` routes to, which is created by the router and takes no templates.\n */\n protected readonly detailPanels = orderSparkExtensions(inject(SPARK_DETAIL_PANELS, { optional: true }), p => p.order);\n protected readonly detailActions = orderSparkExtensions(inject(SPARK_DETAIL_ACTIONS, { optional: true }), a => a.priority ?? 60);\n\n showCustomActions = input(true);\n extraActionsTemplate = input<TemplateRef<void> | null>(null);\n extraContentTemplate = input<TemplateRef<{ $implicit: PersistentObject; entityType: EntityType }> | null>(null);\n\n /**\n * Header slots forwarded to every sub-query card on this page.\n *\n * A structural directive cannot cross a component boundary, and this component is created by\n * the router — in a default app there is no `<spark-po-detail>` tag to project content into.\n * So an app that wants slotted chrome on its sub-queries substitutes its own route component\n * via `SparkRouteConfig.poDetail` and forwards the templates through here. Forwarding a\n * `TemplateRef` as an input is already this component's idiom; see the two above.\n *\n * Each slot may target one query by alias, so a single forwarded set serves a page rendering\n * several sub-queries.\n */\n queryIconTemplate = input<TemplateRef<any> | null>(null);\n queryCaptionTemplate = input<TemplateRef<any> | null>(null);\n queryActionsTemplate = input<TemplateRef<any> | null>(null);\n\n edited = output<void>();\n deleted = output<void>();\n customActionExecuted = output<{ action: CustomActionDefinition; item: PersistentObject }>();\n\n colors = Color;\n errorMessage = signal<string | null>(null);\n entityType = signal<EntityType | null>(null);\n\n /** The sub-queries rendered as cards below the form, in object form (#460, D17). */\n protected readonly subQueries = computed(() => subQueriesOf(this.entityType()));\n allEntityTypes = signal<EntityType[]>([]);\n item = signal<PersistentObject | null>(null);\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n asDetailTypes = signal<Record<string, EntityType>>({});\n asDetailReferenceOptions = signal<Record<string, Record<string, QueryResultItem[]>>>({});\n type = '';\n id = '';\n /** The catalogue's Edit (#467, D8) when this page offers it: listed for the caller, on the detail side, and allowed on this row. */\n editAction = signal<CustomActionDefinition | null>(null);\n /** The catalogue's Delete, on the same terms as {@link editAction}. */\n deleteAction = signal<CustomActionDefinition | null>(null);\n canEdit = computed(() => this.editAction() !== null);\n canDelete = computed(() => this.deleteAction() !== null);\n customActions = signal<CustomActionDefinition[]>([]);\n /** Type-level rights, kept for the add-on context; null until loaded. */\n permissions = signal<EntityPermissions | null>(null);\n\n /**\n * The route's `?deleted=` mode (#460, T2): `only` when the row was opened from the recycle bin.\n * Sent with every load of this object; the server honours it only for `ViewDeleted` holders, so\n * for anyone else a deleted row stays a 404.\n */\n deletedMode = signal<SparkDeletedFilter | null>(null);\n\n /** True when the page shows a soft-deleted row: nothing but Restore/Purge applies to it. */\n protected readonly isDeletedView = computed(() => this.deletedMode() === 'only');\n\n /** What every add-on panel and action receives; rebuilt whenever the object or rights change. */\n protected readonly detailContext = computed((): SparkDetailContext | null => {\n const item = this.item();\n const entityType = this.entityType();\n if (!item || !entityType) return null;\n return {\n type: this.type,\n id: this.id,\n item,\n entityType,\n permissions: this.permissions(),\n deleted: this.deletedMode(),\n reload: () => this.load(),\n };\n });\n\n /**\n * The actions actually offered for the object on screen.\n *\n * The catalogue at `/spark/actions/{objectTypeId}` is per TYPE -- the server is never told which\n * row is open -- so an action that applies to only some rows cannot be filtered there. The\n * entity's actions hook decides while it has the entity in hand and withholds what does not\n * apply, and the object arrives carrying that answer.\n *\n * Since #460 (D13) the server asks the same hook again when the action is submitted and refuses a\n * disabled one with 403, so hiding it here matches what the server will do. What this prevents is\n * offering a destructive action where it cannot possibly apply -- Coverage showed an irreversible\n * red \"Delete data\" button on every repository page, healthy ones included, and only admitted it\n * would refuse after the confirmation prompt.\n */\n visibleCustomActions = computed(() => {\n const withheld = this.item()?.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\n constructor() {\n // The query map too: Restore navigates from `?deleted=only` to the same path without it, and the\n // object must then be re-read as a live row.\n combineLatest([this.route.paramMap, this.route.queryParamMap])\n .pipe(takeUntilDestroyed())\n .subscribe(([params, query]) => this.onParamsChange(params, query.get('deleted')));\n\n // Server-issued `refreshAttribute` patches for the object this page is showing.\n //\n // Tracks the token and the entity type, never `item()`: writing the signal this effect reads\n // would re-run it forever, so the read-modify-write of the object happens untracked.\n effect(() => {\n const objectTypeId = this.entityType()?.id;\n if (!this.attributeRefresh.tokenFor(objectTypeId, this.id)) return;\n\n const patches = this.attributeRefresh.patchesFor(objectTypeId, this.id);\n untracked(() => this.applyAttributePatches(patches));\n });\n }\n\n /**\n * Applies patched attribute values in place, without a re-fetch.\n *\n * The operation carries the value the server computed after its own write, so this is both\n * cheaper and more correct than re-reading: a re-read goes through a RavenDB index that may\n * still be stale from the very write that produced the patch, and would show the old value.\n */\n private applyAttributePatches(patches: Record<string, SparkAttributePatch>): void {\n const current = this.item();\n if (!current) return;\n\n let changed = false;\n const attributes = current.attributes.map(attribute => {\n if (!Object.prototype.hasOwnProperty.call(patches, attribute.name)) return attribute;\n\n const patch = patches[attribute.name];\n const next = { ...attribute };\n let patched = false;\n\n // Each field is applied on its own, because they are not interchangeable: an AsDetail\n // attribute carries its rows in object/objects and leaves value null, so applying only\n // value could never refresh a detail grid.\n //\n // Compared by identity in every case. A scalar skips when unchanged, and a null skips\n // against a null -- which matters because the server writes object/objects as null on a\n // scalar patch, and treating that as a change would repaint on every patch. A rewritten\n // collection is a new array and so never matches, which is the behaviour wanted: comparing\n // rows deeply would cost more than the repaint it saves.\n if (patch.value !== undefined && next.value !== patch.value) {\n next.value = patch.value;\n patched = true;\n }\n // Only for the attributes that actually use them. The server writes object/objects as\n // null on every scalar patch, so applying them unconditionally would mark a scalar\n // attribute changed on each patch and repaint for nothing.\n if (attribute.dataType === 'AsDetail') {\n if (patch.object !== undefined && next.object !== patch.object) {\n next.object = patch.object as PersistentObject | null;\n patched = true;\n }\n if (patch.objects !== undefined && next.objects !== patch.objects) {\n next.objects = patch.objects as PersistentObject[] | null;\n patched = true;\n }\n }\n\n if (!patched) return attribute;\n changed = true;\n return next;\n });\n\n if (changed) this.item.set({ ...current, attributes });\n }\n\n private async onParamsChange(params: any, deleted: string | null = null): Promise<void> {\n this.type = params.get('type') || '';\n this.id = params.get('id') || '';\n const mode = parseSparkDeletedParam(deleted);\n // `exclude` is the default on the wire; keep it out of the request like an absent parameter.\n this.deletedMode.set(mode === 'exclude' ? null : mode);\n await this.load();\n }\n\n /** The object as the page's current mode sees it. Only a set mode is sent. */\n private loadItem(): Promise<PersistentObject> {\n const deleted = this.deletedMode();\n return deleted\n ? this.sparkService.get(this.type, this.id, { deleted })\n : this.sparkService.get(this.type, this.id);\n }\n\n /** (Re)loads the object, its type, rights and actions. Also what add-ons call via `context.reload()`. */\n async load(): Promise<void> {\n this.errorMessage.set(null);\n try {\n const [entityTypes, item] = await Promise.all([\n this.sparkService.getEntityTypes(),\n this.loadItem()\n ]);\n\n const et = entityTypes.find(t => t.id === this.type || t.alias === this.type) || null;\n // Rights and actions are fetched BEFORE the object is published. The component is reused when\n // the route moves to another object, so publishing the new object first would show it under\n // the previous object's Edit/Delete/custom actions until these arrive: a Delete the caller\n // holds on the page they came from flashed on one where they hold none.\n const [permissions, actions] = et\n ? await Promise.all([\n this.sparkService.getPermissions(et.id),\n this.sparkService.getCustomActions(et.id)\n ])\n : [null, null];\n\n this.allEntityTypes.set(entityTypes);\n this.entityType.set(et);\n this.item.set(item);\n this.loadLookupReferenceOptions();\n this.loadAsDetailTypes();\n\n if (!et || !permissions || !actions) {\n this.permissions.set(null);\n this.editAction.set(null);\n this.deleteAction.set(null);\n this.customActions.set([]);\n } else {\n // Prefer the per-row affordances (#236 G5) when the server attached them — a row the\n // caller may read but not mutate then hides its Edit/Delete buttons instead of rendering\n // ones that 404. `can` is absent for types with no row rule, so fall back to type-level.\n // The override (not an intersection) is safe because the server computes `can` as the\n // intersection of type-level rights and the row rule (#243) — it never claims more than\n // `permissions` does.\n const can = item.can;\n // The built-in actions honour `disabledActions` too (#460, D13): the server's\n // OnDisableActionsAsync decides, and refuses a disabled Edit/Save/Delete with 403 at submit,\n // so offering the button would only lead to that refusal. Case-insensitive, like the\n // custom-action filter below.\n const withheld = new Set((item.disabledActions ?? []).map(name => name.toLowerCase()));\n // A row opened from the recycle bin (#460): Edit and Delete judge live rows only — the\n // SoftDelete policy hides a deleted row from both — so they would 404. Restore/Purge come\n // from the soft-delete entry point's detail action instead.\n const deletedView = this.isDeletedView();\n this.permissions.set(permissions);\n // Edit and Delete come from the composed catalogue (#467, D8): listed only when the caller\n // holds Edit/T or Delete/T, shown here when their showedOn includes the detail side (an app\n // removes one with \"Edit\": null), and the row's `can` must allow it too. The selection rule\n // does not apply: the page acts on the one object it shows.\n const edit = builtInAction(actions, 'Edit');\n const del = builtInAction(actions, 'Delete');\n this.editAction.set(edit && isShowedOnDetail(edit.showedOn) && !deletedView\n && (can ? can.edit : permissions.canEdit) && !withheld.has('edit') && !withheld.has('save') ? edit : null);\n this.deleteAction.set(del && isShowedOnDetail(del.showedOn) && !deletedView\n && (can ? can.delete : permissions.canDelete) && !withheld.has('delete') ? del : null);\n // Custom actions only: the built-ins are rendered above as this page's Edit and Delete.\n this.customActions.set(deletedView ? [] : filterDetailActions(actions));\n }\n } catch (e) {\n const error = e as HttpErrorResponse;\n this.errorMessage.set(error.error?.error || error.message || 'An unexpected error occurred');\n }\n }\n\n /**\n * Drawn from the loaded object's own `showedOn`, falling back to the model's (#264, G-Q3): an action sets\n * it per object in `OnLoadAsync` — a stolen car's police report number is shown, the model says `None`.\n */\n visibleAttributes = computed(() => {\n const loaded = new Map((this.item()?.attributes ?? []).map(a => [a.name, a] as const));\n return this.entityType()?.attributes\n .filter(a => hasShowedOnFlag(loaded.get(a.name)?.showedOn ?? a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n private static readonly DEFAULT_TAB: AttributeTab = { id: '__default__', name: 'Algemeen', label: { nl: 'Algemeen', en: 'General' }, order: 0 };\n\n ungroupedAttributes = computed(() => {\n const attrs = this.visibleAttributes();\n const groupIds = new Set((this.entityType()?.groups || []).map(g => g.id));\n return attrs.filter(a => !a.group || !groupIds.has(a.group));\n });\n\n resolvedTabs = computed((): AttributeTab[] => {\n const et = this.entityType();\n const definedTabs = et?.tabs?.length ? [...et.tabs].sort((a, b) => a.order - b.order) : [];\n const hasUngroupedAttrs = this.ungroupedAttributes().length > 0;\n const hasUntabbedGroups = (et?.groups || []).some(g => !g.tab);\n\n if (hasUngroupedAttrs || hasUntabbedGroups || definedTabs.length === 0) {\n return [SparkPoDetailComponent.DEFAULT_TAB, ...definedTabs];\n }\n return definedTabs;\n });\n\n groupsForTab(tab: AttributeTab): AttributeGroup[] {\n const groups = this.entityType()?.groups || [];\n if (tab.id === '__default__') {\n return groups.filter(g => !g.tab).sort((a, b) => a.order - b.order);\n }\n return groups.filter(g => g.tab === tab.id).sort((a, b) => a.order - b.order);\n }\n\n attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[] {\n return this.visibleAttributes().filter(a => a.group === group.id);\n }\n\n /** Whether the attribute's renderer asks for the whole row, without a label (`fullWidth`). */\n isFullWidthRenderer(attr: EntityAttributeDefinition): boolean {\n if (!attr.renderer) return false;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.fullWidth === true;\n }\n\n getDetailRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n return this.rendererRegistry.find(r => r.name === attr.renderer)?.detailComponent ?? null;\n }\n\n /** Inputs of an AsDetail row renderer (`rowComponent`): the row and the object that owns it. */\n getRowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>, item: PersistentObject): Record<string, any> {\n return rowRendererInputs(renderer, row, item?.id);\n }\n\n getDetailRendererInputs(component: Type<any>, attr: EntityAttributeDefinition, item: PersistentObject): Record<string, any> {\n const itemAttr = item.attributes.find(a => a.name === attr.name);\n const formData: Record<string, any> = {};\n for (const a of item.attributes) {\n formData[a.name] = rendererValue(a);\n }\n return withDeclaredInputs(component, {\n value: rendererValue(itemAttr),\n attribute: attr,\n options: attr.rendererOptions,\n formData,\n item,\n });\n }\n\n /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */\n getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.columnComponent ?? null;\n }\n\n getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n });\n }\n\n private async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.visibleAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n // A lookup that fails to load only costs its labels: the attribute then shows its raw value\n // (attributeValue's fallback) instead of the whole page erroring over a secondary request (#453).\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const result = await this.sparkService.getLookupReference(name).catch(() => null);\n return [name, result] as const;\n })\n );\n this.lookupReferenceOptions.set(entries.reduce(\n (acc, [k, v]) => v ? { ...acc, [k]: v } : acc,\n {} as Record<string, LookupReference>));\n }\n\n private async loadAsDetailTypes(): Promise<void> {\n const asDetailAttrs = this.visibleAttributes().filter(a => a.dataType === 'AsDetail' && a.isArray && a.asDetailType);\n if (asDetailAttrs.length === 0) return;\n\n const types = this.allEntityTypes();\n const newAsDetailTypes: Record<string, EntityType> = {};\n // Same fallback as the form: a row type is usually absent from the Query-gated catalogue, and\n // the server now ships its definition alongside the parent (#385).\n const embedded = this.entityType()?.detailTypes ?? [];\n\n for (const attr of asDetailAttrs) {\n const asDetailType =\n types.find(t => t.clrType === attr.asDetailType)\n ?? embedded.find(t => t.clrType === attr.asDetailType);\n if (asDetailType) {\n newAsDetailTypes[attr.name] = asDetailType;\n const refCols = asDetailType.attributes.filter(a => a.dataType === 'Reference' && a.query);\n if (refCols.length > 0) {\n const refEntries = await Promise.all(\n refCols.map(async col => {\n const result = await this.sparkService.executeQueryByName(col.query!, {\n parentId: this.id,\n parentType: this.type,\n });\n return [col.name, result.items] as const;\n })\n );\n this.asDetailReferenceOptions.update(prev => ({\n ...prev,\n [attr.name]: refEntries.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {} as Record<string, QueryResultItem[]>)\n }));\n }\n }\n }\n this.asDetailTypes.set(newAsDetailTypes);\n }\n\n /**\n * The bootstrap classes for a custom action's button. Outline-primary stays the default, so\n * every existing action looks exactly as it did; a server-declared variant opts one action into\n * a louder style. Restricted to a known set rather than interpolated, because the value arrives\n * from a JSON file and must not be able to put arbitrary classes on the button.\n */\n /** Edit's and Delete's button classes: the catalogue's variant, else the look they always had. */\n protected builtInActionClass(action: CustomActionDefinition, fallback: 'primary' | 'danger'): string {\n const variant = action.variant?.toLowerCase();\n return ['danger', 'warning', 'primary', 'secondary', 'success'].includes(variant ?? '')\n ? `btn btn-${variant}`\n : `btn btn-${fallback}`;\n }\n\n protected customActionClass(action: CustomActionDefinition): string {\n const variant = action.variant?.toLowerCase();\n switch (variant) {\n case 'danger':\n case 'warning':\n case 'primary':\n case 'secondary':\n case 'success':\n return `btn btn-${variant}`;\n default:\n return 'btn btn-outline-primary';\n }\n }\n\n /**\n * The action currently running, or null. Drives the disabled state on every custom-action\n * button.\n *\n * A custom action is not necessarily quick: Coverage's Resync makes paged GitHub App calls for\n * every account the caller manages, inside the request. Without this the button stayed live and\n * looked inert for several seconds, and a second click queued a second full run -- which is how\n * a slow action and a broken one became indistinguishable.\n */\n runningAction = signal<string | null>(null);\n\n /**\n * Why an action cannot run in this browser, translated, for its tooltip: the client method it\n * requires (`requiresClient`) is not registered or not supported here (Q9). Null otherwise.\n */\n protected customActionUnavailableReason(action: CustomActionDefinition): string | null {\n const key = this.clientMethods.unavailableReason(action.requiresClient);\n return key === null ? null : this.lang.t(key);\n }\n\n async onCustomAction(action: CustomActionDefinition): Promise<void> {\n // Guard re-entry as well as disabling the button: the template is not the only caller, and a\n // host component driving this method directly would otherwise bypass the check.\n if (this.runningAction()) return;\n if (this.clientMethods.unavailableReason(action.requiresClient) !== null) return;\n\n const message = confirmationText(action, 1);\n if (message && !confirm(message)) return;\n\n this.runningAction.set(action.name);\n try {\n await this.sparkService.executeCustomAction(this.type, action.name, this.item() || undefined);\n this.customActionExecuted.emit({ action, item: this.item()! });\n if (action.refreshOnCompleted) {\n // The sub-query grids below do not depend on item(), so re-fetching the PO left them\n // showing pre-action rows -- the action appeared to have done nothing to the very\n // lists it changed.\n //\n // Before the await, not after (#319): a server-issued refreshQuery was already dispatched\n // inside executeCustomAction, and bumps in one synchronous run coalesce into one grid\n // fetch. Bumping after the re-fetch's network round trip landed in a second\n // change-detection flush, and the grid fetched twice for one click.\n for (const subQuery of this.subQueries()) {\n this.queryRefresh.request(subQuery.query);\n }\n\n const item = await this.loadItem();\n this.item.set(item);\n }\n } catch (e) {\n const err = e as HttpErrorResponse;\n this.errorMessage.set(err.error?.error || err.message || 'Action failed');\n } finally {\n // finally, not after the try: an action that fails must not leave every button on the page\n // permanently disabled.\n this.runningAction.set(null);\n }\n }\n\n onEdit(): void {\n this.edited.emit();\n this.router.navigate(['/po', this.type, this.id, 'edit']);\n }\n\n async onDelete(): Promise<void> {\n // The version on screen is the one deleted (#467, D14): a row changed since is a 409.\n const etag = this.item()?.etag;\n if (!etag) return;\n const message = confirmationText(this.deleteAction(), 1);\n if (!message || confirm(message)) {\n await this.sparkService.delete(this.type, this.id, etag);\n this.deleted.emit();\n // Back to the list the row was opened from (else the type's list), not the start page.\n await this.returnNavigation.returnToList(this.entityType()?.name);\n }\n }\n\n onBack(): void {\n window.history.back();\n }\n}\n","<bs-container>\n<div class=\"container\">\n @if (errorMessage(); as err) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">\n {{ err }}\n </bs-alert>\n } @else if (item(); as currentItem) {\n @if (entityType(); as et) {\n <div class=\"spark-actionbar py-2\">\n <bs-priority-nav [moreLabel]=\"lang.t('common.more')\" [collapseAt]=\"'sm'\">\n <button *bsPriorityNavItem=\"1\" class=\"btn btn-outline-secondary\" (click)=\"onBack()\">\n <spark-icon name=\"arrow-left\" /> {{ 'common.back' | t }}\n </button>\n <!-- Edit and Delete are the composed catalogue's (#467, D8): label, icon and variant included. -->\n @if (editAction(); as edit) {\n <button *bsPriorityNavItem=\"2\" [class]=\"builtInActionClass(edit, 'primary')\" (click)=\"onEdit()\">\n @if (edit.icon) { <spark-icon [name]=\"edit.icon\" /> } {{ edit.label | resolveTranslation }}\n </button>\n }\n @if (deleteAction(); as del) {\n <button *bsPriorityNavItem=\"3\" [class]=\"builtInActionClass(del, 'danger')\" (click)=\"onDelete()\">\n @if (del.icon) { <spark-icon [name]=\"del.icon\" /> } {{ del.label | resolveTranslation }}\n </button>\n }\n @if (showCustomActions()) {\n @for (action of visibleCustomActions(); track action.name) {\n @let unavailable = customActionUnavailableReason(action);\n <button *bsPriorityNavItem=\"10 + action.offset\" [class]=\"customActionClass(action)\" [attr.data-action]=\"action.name\"\n [disabled]=\"runningAction() !== null || unavailable !== null\" [attr.title]=\"unavailable\" (click)=\"onCustomAction(action)\">\n {{ action.label | resolveTranslation }}\n </button>\n }\n }\n @if (extraActionsTemplate(); as extraActionsTpl) {\n <ng-container *bsPriorityNavItem=\"50\">\n <ng-container *ngTemplateOutlet=\"extraActionsTpl\"></ng-container>\n </ng-container>\n }\n <!-- Add-on actions (SPARK_DETAIL_ACTIONS): each decides for itself whether it applies. -->\n @if (detailContext(); as ctx) {\n @for (action of detailActions; track action.id) {\n <ng-container *bsPriorityNavItem=\"action.priority ?? 60\">\n <ng-container *ngComponentOutlet=\"action.component; inputs: { context: ctx }\" />\n </ng-container>\n }\n }\n </bs-priority-nav>\n </div>\n <h2>\n {{ currentItem.breadcrumb || currentItem.name }}\n @if (isDeletedView()) {\n <bs-badge [type]=\"colors.secondary\" class=\"align-middle ms-2 spark-deleted-badge\">{{ 'softDelete.deleted' | t }}</bs-badge>\n }\n </h2>\n\n <bs-grid>\n <bs-tab-control>\n @for (tab of resolvedTabs(); track tab.id) {\n <bs-tab-page>\n <ng-template bsTabPageHeader>{{ tab.label | resolveTranslation:tab.name }}</ng-template>\n <ng-container *ngTemplateOutlet=\"detailTabContent; context: { $implicit: tab }\"></ng-container>\n </bs-tab-page>\n }\n </bs-tab-control>\n\n <ng-template #detailTabContent let-tab>\n @if (tab.id === '__default__') {\n @let ungroupedAttrs = ungroupedAttributes();\n @if (ungroupedAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n <div class=\"p-3\">\n <dl bsRow>\n @for (attr of ungroupedAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"detailAttrField; context: { $implicit: attr, item: currentItem }\"></ng-container>\n }\n </dl>\n </div>\n </bs-card>\n }\n }\n @for (group of groupsForTab(tab); track group.id) {\n @if (attrsForGroup(group); as groupAttrs) {\n @if (groupAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n @if (group.label) {\n <bs-card-header>{{ group.label | resolveTranslation:group.name }}</bs-card-header>\n }\n <div class=\"p-3\">\n <dl bsRow>\n @for (attr of groupAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"detailAttrField; context: { $implicit: attr, item: currentItem }\"></ng-container>\n }\n </dl>\n </div>\n </bs-card>\n }\n }\n }\n </ng-template>\n\n <ng-template #detailAttrField let-attr let-currentItem=\"item\">\n <!-- A full-width renderer (the core 'paragraph') is body text: no label, the whole row. -->\n @let fullWidth = isFullWidthRenderer(attr);\n @if (!fullWidth) {\n <dt [sm]=\"3\">{{ (attr.label | resolveTranslation) || attr.name }}<spark-attribute-description [description]=\"attr.description\" /></dt>\n }\n <dd [sm]=\"fullWidth ? 12 : 9\">\n @if (getDetailRendererComponent(attr); as rendererType) {\n <ng-container *ngComponentOutlet=\"rendererType; inputs: getDetailRendererInputs(rendererType, attr, currentItem)\"></ng-container>\n } @else if (attr.dataType === 'AsDetail' && attr.isArray) {\n <bs-table [isResponsive]=\"true\">\n <thead>\n <tr>\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ (col.label | resolveTranslation) || col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n </tr>\n </thead>\n <tbody class=\"align-middle\">\n @let rowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n @for (row of (attr.name | arrayValue:currentItem); track $index) {\n <tr>\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <td>\n <!--\n Shared with the query grid. This cell used to hand-roll the renderer\n dispatch and print everything else as text, so a `boolean` column read\n \"true\" here and rendered a checkbox in a grid, and a `color` showed its\n hex. Only the VALUE is resolved locally: these rows are plain\n dictionaries, not PersistentObjects.\n\n `chips` is left unset for a reference array on purpose — an AsDetail\n row's `__sparkBreadcrumbs` is keyed by column, not by id, so there are\n no labels for the members. Text beats a row of raw ids.\n -->\n <spark-grid-cell\n [column]=\"col\"\n [display]=\"(row | asDetailCellValue:attr:col:asDetailReferenceOptions())\"\n [rendererValue]=\"row[col.name]\"\n [item]=\"row\"\n [link]=\"col.dataType === 'Reference' && col.referenceType\n ? (col.referenceType | referenceLinkRoute:row[col.name]:allEntityTypes())\n : null\" />\n <!-- A row renderer (rowComponent: contributions' attribution line) draws\n its attributes once, under the row's first cell, instead of as columns. -->\n @if (firstCol && rowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"rowRenderer.component; inputs: getRowRendererInputs(rowRenderer, row, currentItem)\"></ng-container>\n </div>\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n } @else if (attr.dataType === 'boolean') {\n <input type=\"checkbox\"\n [checked]=\"(attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) === true\"\n [indeterminate]=\"(attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) == null\"\n disabled\n onclick=\"return false;\"\n class=\"opacity-100\">\n } @else if (attr.dataType === 'color') {\n @let colorVal = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (colorVal) {\n <span class=\"spark-color-swatch d-inline-block align-middle border rounded me-2\" [style.background-color]=\"colorVal\"></span>\n {{ colorVal }}\n } @else {\n -\n }\n } @else if (attr.dataType === 'image') {\n @let imageSrc = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (imageSrc) {\n <img [src]=\"imageSrc\" [alt]=\"(attr.label | resolveTranslation) || attr.name\" loading=\"lazy\"\n class=\"img-fluid rounded\" style=\"max-height: 12rem;\">\n } @else {\n -\n }\n } @else if (attr.dataType === 'url') {\n @let hrefVal = (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes());\n @if (hrefVal) {\n <a [href]=\"hrefVal\" target=\"_blank\" rel=\"noopener noreferrer\">{{ hrefVal }}</a>\n } @else {\n -\n }\n } @else if (attr.dataType === 'Reference' && attr.isArray) {\n @let chips = (attr.name | referenceChips:item());\n @if (chips.length) {\n <div class=\"d-flex flex-wrap gap-1\">\n @for (chip of chips; track chip.id) {\n @let chipRoute = (attr.referenceType ? (attr.referenceType | referenceLinkRoute:chip.id:allEntityTypes()) : null);\n @if (chipRoute) {\n <a class=\"spark-chip\" [routerLink]=\"chipRoute\">{{ chip.label }}</a>\n } @else {\n <span class=\"spark-chip\">{{ chip.label }}</span>\n }\n }\n </div>\n } @else {\n -\n }\n } @else if (attr.dataType === 'Reference' && attr.referenceType) {\n @let refRoute = (attr.referenceType | referenceLinkRoute:(attr.name | rawAttributeValue:item()):allEntityTypes());\n @if (refRoute && (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes())) {\n <a [routerLink]=\"refRoute\">{{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) }}</a>\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n } @else if (attr.dataType === 'MultiLineString') {\n <div class=\"spark-multiline-value\">{{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}</div>\n } @else if (attr.dataType === 'date' || attr.dataType === 'datetime') {\n <!--\n Formatted the same way, and in the same timezone, as the grid renders it.\n\n Before this, a detail page printed the raw wire value — `2026-12-31T23:59:00-08:00`\n in a definition list — while the grid showed `01/01/2027, 08:59` for the same\n document. Not a rounding difference: a DateTimeOffset whose offset differs from the\n viewer's can disagree on the DATE, so the two pages named different days for one car.\n\n The grid's meaning wins because it is the one a reader can act on: a timestamp is an\n instant, shown in the viewer's own zone. The originating offset is still on the wire\n for code that needs it — server-side logic, exports, \"which country's morning was\n this?\" — it simply is not what a viewer is shown by default.\n -->\n @let when = (attr.name | rawAttributeValue:item()) | parsedDate;\n @if (when) {\n {{ when | date:(attr.dataType === 'date' ? 'mediumDate' : 'short') }}\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n } @else {\n {{ (attr.name | attributeValue:item():entityType():lookupReferenceOptions():allEntityTypes()) || '-' }}\n }\n </dd>\n </ng-template>\n </bs-grid>\n @for (subQuery of subQueries(); track subQuery.query) {\n <!-- The entry's selectionMode overrides the query's own for this parent (#460, D17). -->\n <spark-query-card [queryId]=\"subQuery.query\" [parentId]=\"currentItem.id!\" [parentType]=\"et.name\"\n [selectionMode]=\"subQuery.selectionMode ?? null\"\n [parentDeleted]=\"isDeletedView()\"\n [iconTemplate]=\"queryIconTemplate()\" [captionTemplate]=\"queryCaptionTemplate()\"\n [actionsTemplate]=\"queryActionsTemplate()\" />\n }\n @if (extraContentTemplate(); as extraContentTpl) {\n <ng-container *ngTemplateOutlet=\"extraContentTpl; context: { $implicit: currentItem, entityType: et }\"></ng-container>\n }\n <!-- Add-on panels (SPARK_DETAIL_PANELS), e.g. <spark-po-history>. -->\n @if (detailContext(); as ctx) {\n @for (panel of detailPanels; track panel.id) {\n <div class=\"spark-detail-panel m-3\">\n <ng-container *ngComponentOutlet=\"panel.component; inputs: { context: ctx }\" />\n </div>\n }\n }\n }\n } @else {\n <div class=\"text-center p-5\">\n <bs-spinner />\n </div>\n }\n</div>\n</bs-container>\n"],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ import * as i0 from "@angular/core";
2
+ import { ApplicationRef, DestroyRef, Injectable, InjectionToken, PLATFORM_ID, inject, isDevMode, makeEnvironmentProviders, provideAppInitializer, signal } from "@angular/core";
3
+ import { SwUpdate, provideServiceWorker } from "@angular/service-worker";
4
+ import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
5
+ import { NavigationStart, Router } from "@angular/router";
6
+ import { Subscription, filter, first } from "rxjs";
7
+ const SPARK_SW_PAGE_LOADER = new InjectionToken("SPARK_SW_PAGE_LOADER", {
8
+ providedIn: "root",
9
+ factory: () => {
10
+ const location = inject(DOCUMENT).defaultView?.location;
11
+ return {
12
+ assign: (url) => location?.assign(url),
13
+ reload: () => location?.reload()
14
+ };
15
+ }
16
+ });
17
+ const SPARK_SW_SCHEDULE = new InjectionToken("SPARK_SW_SCHEDULE", {
18
+ providedIn: "root",
19
+ factory: () => ({
20
+ checkIntervalMs: 216e5,
21
+ visibleCheckThrottleMs: 6e4
22
+ })
23
+ });
24
+ const RELOADED_FOR_KEY = "spark-sw-reloaded-for";
25
+ var SparkServiceWorkerUpdates = class SparkServiceWorkerUpdates {
26
+ swUpdate = inject(SwUpdate);
27
+ router = inject(Router, { optional: true });
28
+ location = inject(Location, { optional: true });
29
+ appRef = inject(ApplicationRef);
30
+ document = inject(DOCUMENT);
31
+ platformId = inject(PLATFORM_ID);
32
+ destroyRef = inject(DestroyRef);
33
+ pageLoader = inject(SPARK_SW_PAGE_LOADER);
34
+ schedule = inject(SPARK_SW_SCHEDULE);
35
+ _updateReady = signal(false, ...ngDevMode ? [{ debugName: "_updateReady" }] : /* istanbul ignore next */ []);
36
+ _currentVersion = signal(null, ...ngDevMode ? [{ debugName: "_currentVersion" }] : /* istanbul ignore next */ []);
37
+ _latestVersion = signal(null, ...ngDevMode ? [{ debugName: "_latestVersion" }] : /* istanbul ignore next */ []);
38
+ updateReady = this._updateReady.asReadonly();
39
+ currentVersion = this._currentVersion.asReadonly();
40
+ latestVersion = this._latestVersion.asReadonly();
41
+ started = false;
42
+ leaving = false;
43
+ reloadWhenVisible = false;
44
+ lastCheck = 0;
45
+ start() {
46
+ if (this.started) return;
47
+ this.started = true;
48
+ if (!isPlatformBrowser(this.platformId) || !this.swUpdate.isEnabled) return;
49
+ const subscriptions = new Subscription();
50
+ this.destroyRef.onDestroy(() => subscriptions.unsubscribe());
51
+ subscriptions.add(this.swUpdate.versionUpdates.pipe(filter((event) => event.type === "VERSION_READY")).subscribe((event) => this.onVersionReady(event)));
52
+ subscriptions.add(this.swUpdate.unrecoverable.subscribe(() => this.onUnrecoverable()));
53
+ if (this.router) subscriptions.add(this.router.events.pipe(filter((event) => event instanceof NavigationStart)).subscribe((event) => this.onNavigationStart(event)));
54
+ subscriptions.add(this.appRef.isStable.pipe(first((stable) => stable)).subscribe(() => {
55
+ this.check();
56
+ const interval = setInterval(() => this.check(), this.schedule.checkIntervalMs);
57
+ subscriptions.add(() => clearInterval(interval));
58
+ }));
59
+ const onVisibilityChange = () => {
60
+ if (this.isHidden()) return;
61
+ if (this.reloadWhenVisible) {
62
+ this.reloadWhenVisible = false;
63
+ this.reload();
64
+ return;
65
+ }
66
+ if (Date.now() - this.lastCheck >= this.schedule.visibleCheckThrottleMs) this.check();
67
+ };
68
+ this.document.addEventListener("visibilitychange", onVisibilityChange);
69
+ subscriptions.add(() => this.document.removeEventListener("visibilitychange", onVisibilityChange));
70
+ }
71
+ onVersionReady(event) {
72
+ const latest = toVersion(event.latestVersion);
73
+ this._currentVersion.set(toVersion(event.currentVersion));
74
+ this._latestVersion.set(latest);
75
+ if (this.readReloadedFor() === latest.hash) return;
76
+ this._updateReady.set(true);
77
+ }
78
+ onUnrecoverable() {
79
+ if (this.isHidden()) {
80
+ this.reloadWhenVisible = true;
81
+ return;
82
+ }
83
+ this.reload();
84
+ }
85
+ onNavigationStart(event) {
86
+ if (!this._updateReady() || this.leaving || this.isHidden()) return;
87
+ this.leaving = true;
88
+ this.writeReloadedFor(this._latestVersion()?.hash);
89
+ this.router?.currentNavigation()?.abort();
90
+ if (event.navigationTrigger === "popstate") {
91
+ this.pageLoader.reload();
92
+ return;
93
+ }
94
+ const target = this.location ? this.location.prepareExternalUrl(event.url) : event.url;
95
+ this.pageLoader.assign(target);
96
+ }
97
+ check() {
98
+ this.lastCheck = Date.now();
99
+ this.swUpdate.checkForUpdate().catch(() => {});
100
+ }
101
+ reload() {
102
+ if (this.leaving) return;
103
+ this.leaving = true;
104
+ this.pageLoader.reload();
105
+ }
106
+ isHidden() {
107
+ return this.document.visibilityState === "hidden";
108
+ }
109
+ readReloadedFor() {
110
+ try {
111
+ return this.document.defaultView?.sessionStorage.getItem(RELOADED_FOR_KEY) ?? null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+ writeReloadedFor(hash) {
117
+ if (!hash) return;
118
+ try {
119
+ this.document.defaultView?.sessionStorage.setItem(RELOADED_FOR_KEY, hash);
120
+ } catch {}
121
+ }
122
+ static ɵfac = i0.ɵɵngDeclareFactory({
123
+ minVersion: "12.0.0",
124
+ version: "22.2.0",
125
+ ngImport: i0,
126
+ type: SparkServiceWorkerUpdates,
127
+ deps: [],
128
+ target: i0.ɵɵFactoryTarget.Injectable
129
+ });
130
+ static ɵprov = i0.ɵɵngDeclareInjectable({
131
+ minVersion: "12.0.0",
132
+ version: "22.2.0",
133
+ ngImport: i0,
134
+ type: SparkServiceWorkerUpdates,
135
+ providedIn: "root"
136
+ });
137
+ };
138
+ i0.ɵɵngDeclareClassMetadata({
139
+ minVersion: "12.0.0",
140
+ version: "22.2.0",
141
+ ngImport: i0,
142
+ type: SparkServiceWorkerUpdates,
143
+ decorators: [{
144
+ type: Injectable,
145
+ args: [{ providedIn: "root" }]
146
+ }]
147
+ });
148
+ function toVersion(version) {
149
+ return {
150
+ hash: version.hash,
151
+ appData: version.appData
152
+ };
153
+ }
154
+ function provideSparkServiceWorker(options = {}) {
155
+ return makeEnvironmentProviders([provideServiceWorker(options.script ?? "ngsw-worker.js", {
156
+ enabled: options.enabled ?? !isDevMode(),
157
+ registrationStrategy: options.registrationStrategy ?? "registerWhenStable:30000"
158
+ }), provideAppInitializer(() => inject(SparkServiceWorkerUpdates).start())]);
159
+ }
160
+ export { SPARK_SW_PAGE_LOADER, SPARK_SW_SCHEDULE, SparkServiceWorkerUpdates, provideSparkServiceWorker };
161
+
162
+ //# sourceMappingURL=mintplayer-ng-spark-pwa.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintplayer-ng-spark-pwa.mjs","names":[],"sources":["../../pwa/src/spark-service-worker-updates.ts","../../pwa/src/provide-spark-service-worker.ts"],"sourcesContent":["import { DOCUMENT, Location, isPlatformBrowser } from '@angular/common';\nimport {\n ApplicationRef,\n DestroyRef,\n Injectable,\n InjectionToken,\n PLATFORM_ID,\n inject,\n signal,\n} from '@angular/core';\nimport { NavigationStart, Router } from '@angular/router';\nimport { SwUpdate, VersionReadyEvent } from '@angular/service-worker';\nimport { Subscription, filter, first } from 'rxjs';\n\n/** A service-worker version as `ngsw.json` describes it. `appData` is the config's `appData`. */\nexport interface SparkAppVersion {\n hash: string;\n appData?: Record<string, unknown>;\n}\n\n/** The two page loads the update policy performs. Replaceable for tests. */\nexport interface SparkPageLoader {\n assign(url: string): void;\n reload(): void;\n}\n\n/** How the update policy loads a page: `window.location` by default. */\nexport const SPARK_SW_PAGE_LOADER = new InjectionToken<SparkPageLoader>('SPARK_SW_PAGE_LOADER', {\n providedIn: 'root',\n factory: () => {\n const location = inject(DOCUMENT).defaultView?.location;\n return {\n assign: (url: string) => location?.assign(url),\n reload: () => location?.reload(),\n };\n },\n});\n\n/** Update-check timing. Defaults: every 6 h, and on becoming visible at most once a minute. */\nexport interface SparkServiceWorkerSchedule {\n checkIntervalMs: number;\n visibleCheckThrottleMs: number;\n}\n\nexport const SPARK_SW_SCHEDULE = new InjectionToken<SparkServiceWorkerSchedule>('SPARK_SW_SCHEDULE', {\n providedIn: 'root',\n factory: () => ({ checkIntervalMs: 6 * 60 * 60 * 1000, visibleCheckThrottleMs: 60 * 1000 }),\n});\n\n/**\n * Remembers, per tab, the version a full load was made for. If the page that load produced still\n * reports the same version as ready (another tab keeps the old version alive, or the worker did not\n * hand this client the new one), the policy does not reload again: one attempt per version.\n */\nconst RELOADED_FOR_KEY = 'spark-sw-reloaded-for';\n\n/**\n * The update policy of `provideSparkServiceWorker()` (#464, Q1b).\n *\n * - `VERSION_READY`: the **next** router navigation becomes a full page load of its target, so the\n * user lands on the page they asked for, in the new version, without losing anything on the page\n * they are leaving (the navigation was leaving it anyway).\n * - `unrecoverable`: the running version can no longer be served, so the page reloads at once.\n * - Checks: once the application is stable, every 6 h, and when the tab becomes visible again\n * (at most once a minute).\n * - **A hidden tab never reloads.** Spark has no unsaved-changes tracking to consult, so a reload\n * nobody is looking at could throw away an open edit. An `unrecoverable` in a hidden tab waits\n * until the tab is visible; a navigation in a hidden tab stays a normal in-app navigation.\n *\n * Nothing happens when the service worker is disabled (development builds, unsupported browsers)\n * or on the server.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkServiceWorkerUpdates {\n private readonly swUpdate = inject(SwUpdate);\n private readonly router = inject(Router, { optional: true });\n private readonly location = inject(Location, { optional: true });\n private readonly appRef = inject(ApplicationRef);\n private readonly document = inject(DOCUMENT);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly destroyRef = inject(DestroyRef);\n private readonly pageLoader = inject(SPARK_SW_PAGE_LOADER);\n private readonly schedule = inject(SPARK_SW_SCHEDULE);\n\n private readonly _updateReady = signal(false);\n private readonly _currentVersion = signal<SparkAppVersion | null>(null);\n private readonly _latestVersion = signal<SparkAppVersion | null>(null);\n\n /** True once a new version is downloaded and the next navigation will load it. */\n readonly updateReady = this._updateReady.asReadonly();\n /** The running version, known once an update was reported. */\n readonly currentVersion = this._currentVersion.asReadonly();\n /** The version the next navigation loads (`appData.build` is the ng-spark version). */\n readonly latestVersion = this._latestVersion.asReadonly();\n\n private started = false;\n private leaving = false;\n private reloadWhenVisible = false;\n private lastCheck = 0;\n\n /** Starts the policy. Idempotent; called by the app initializer `provideSparkServiceWorker()` adds. */\n start(): void {\n if (this.started) return;\n this.started = true;\n\n if (!isPlatformBrowser(this.platformId) || !this.swUpdate.isEnabled) return;\n\n const subscriptions = new Subscription();\n this.destroyRef.onDestroy(() => subscriptions.unsubscribe());\n\n subscriptions.add(\n this.swUpdate.versionUpdates\n .pipe(filter((event): event is VersionReadyEvent => event.type === 'VERSION_READY'))\n .subscribe((event) => this.onVersionReady(event)),\n );\n\n subscriptions.add(this.swUpdate.unrecoverable.subscribe(() => this.onUnrecoverable()));\n\n if (this.router) {\n subscriptions.add(\n this.router.events\n .pipe(filter((event): event is NavigationStart => event instanceof NavigationStart))\n .subscribe((event) => this.onNavigationStart(event)),\n );\n }\n\n // The first check waits for stability, so it never competes with the initial render.\n subscriptions.add(\n this.appRef.isStable.pipe(first((stable) => stable)).subscribe(() => {\n this.check();\n const interval = setInterval(() => this.check(), this.schedule.checkIntervalMs);\n subscriptions.add(() => clearInterval(interval));\n }),\n );\n\n const onVisibilityChange = () => {\n if (this.isHidden()) return;\n if (this.reloadWhenVisible) {\n this.reloadWhenVisible = false;\n this.reload();\n return;\n }\n if (Date.now() - this.lastCheck >= this.schedule.visibleCheckThrottleMs) this.check();\n };\n this.document.addEventListener('visibilitychange', onVisibilityChange);\n subscriptions.add(() => this.document.removeEventListener('visibilitychange', onVisibilityChange));\n }\n\n private onVersionReady(event: VersionReadyEvent): void {\n const latest = toVersion(event.latestVersion);\n this._currentVersion.set(toVersion(event.currentVersion));\n this._latestVersion.set(latest);\n\n if (this.readReloadedFor() === latest.hash) {\n // This page is the full load made for that version and it is still not the one served.\n // Reloading again would loop; the next deploy, or the next fresh tab, picks it up.\n return;\n }\n this._updateReady.set(true);\n }\n\n private onUnrecoverable(): void {\n if (this.isHidden()) {\n this.reloadWhenVisible = true;\n return;\n }\n this.reload();\n }\n\n private onNavigationStart(event: NavigationStart): void {\n if (!this._updateReady() || this.leaving || this.isHidden()) return;\n\n this.leaving = true;\n this.writeReloadedFor(this._latestVersion()?.hash);\n this.router?.currentNavigation()?.abort();\n\n if (event.navigationTrigger === 'popstate') {\n // Back/forward: the address bar already shows the target, and assign() would add a\n // duplicate history entry.\n this.pageLoader.reload();\n return;\n }\n\n // event.url is relative to the router's base; prepareExternalUrl adds the <base href>.\n const target = this.location ? this.location.prepareExternalUrl(event.url) : event.url;\n this.pageLoader.assign(target);\n }\n\n private check(): void {\n this.lastCheck = Date.now();\n this.swUpdate.checkForUpdate().catch(() => {\n // Offline, or the worker is gone. The next scheduled check tries again.\n });\n }\n\n private reload(): void {\n if (this.leaving) return;\n this.leaving = true;\n this.pageLoader.reload();\n }\n\n private isHidden(): boolean {\n return this.document.visibilityState === 'hidden';\n }\n\n private readReloadedFor(): string | null {\n try {\n return this.document.defaultView?.sessionStorage.getItem(RELOADED_FOR_KEY) ?? null;\n } catch {\n return null;\n }\n }\n\n private writeReloadedFor(hash: string | undefined): void {\n if (!hash) return;\n try {\n this.document.defaultView?.sessionStorage.setItem(RELOADED_FOR_KEY, hash);\n } catch {\n // Storage blocked: the guard is best effort; one reload per navigation is still bounded.\n }\n }\n}\n\nfunction toVersion(version: { hash: string; appData?: object }): SparkAppVersion {\n return { hash: version.hash, appData: version.appData as Record<string, unknown> | undefined };\n}\n","import { EnvironmentProviders, inject, isDevMode, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';\nimport { SwRegistrationOptions, provideServiceWorker } from '@angular/service-worker';\n\nimport { SparkServiceWorkerUpdates } from './spark-service-worker-updates';\n\nexport interface SparkServiceWorkerOptions {\n /** Whether the worker registers. Default: `!isDevMode()`, so only production builds register it. */\n enabled?: boolean;\n /** The worker script, relative to the base href. Default: `ngsw-worker.js`. */\n script?: string;\n /** When the worker registers. Default: `registerWhenStable:30000`. */\n registrationStrategy?: SwRegistrationOptions['registrationStrategy'];\n}\n\n/**\n * Registers Angular's service worker with Spark's update policy (#464, Q1b); see\n * {@link SparkServiceWorkerUpdates} for what happens when a new version is deployed.\n *\n * The application also needs an `ngsw-config.json` with Spark's navigation exclusions and\n * `serviceWorker` in its production build configuration; see `docs/guide-pwa.md`.\n */\nexport function provideSparkServiceWorker(options: SparkServiceWorkerOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideServiceWorker(options.script ?? 'ngsw-worker.js', {\n enabled: options.enabled ?? !isDevMode(),\n registrationStrategy: options.registrationStrategy ?? 'registerWhenStable:30000',\n }),\n provideAppInitializer(() => inject(SparkServiceWorkerUpdates).start()),\n ]);\n}\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { Injectable, NgZone, PLATFORM_ID, inject, signal } from "@angular/core";
3
+ import { DOCUMENT, isPlatformBrowser } from "@angular/common";
3
4
  import { HttpClient, withInterceptors } from "@angular/common/http";
4
5
  import { Observable, firstValueFrom } from "rxjs";
5
6
  import { SPARK_RETRY_CANCEL, currentLanguage, filterQueryActions } from "@mintplayer/ng-spark/models";
6
7
  import { SPARK_CONFIG } from "@mintplayer/ng-spark";
7
8
  import { SparkClientMethodRegistry, SparkClientOperationDispatcher } from "@mintplayer/ng-spark/client-operations";
8
9
  import { DomSanitizer } from "@angular/platform-browser";
9
- import { DOCUMENT, isPlatformBrowser } from "@angular/common";
10
10
  import { Router } from "@angular/router";
11
11
  var RetryActionService = class RetryActionService {
12
12
  resolveRetry = null;
@@ -48,9 +48,11 @@ i0.ɵɵngDeclareClassMetadata({
48
48
  args: [{ providedIn: "root" }]
49
49
  }]
50
50
  });
51
+ const LANGUAGE_COOKIE_MAX_AGE = 31536e3;
51
52
  var SparkLanguageService = class SparkLanguageService {
52
53
  http = inject(HttpClient);
53
54
  config = inject(SPARK_CONFIG, { optional: true });
55
+ document = isPlatformBrowser(inject(PLATFORM_ID)) ? inject(DOCUMENT) : null;
54
56
  baseUrl = this.config?.baseUrl ?? "/spark";
55
57
  currentLang = signal("en", ...ngDevMode ? [{ debugName: "currentLang" }] : /* istanbul ignore next */ []);
56
58
  translationsMap = signal({}, ...ngDevMode ? [{ debugName: "translationsMap" }] : /* istanbul ignore next */ []);
@@ -66,6 +68,13 @@ var SparkLanguageService = class SparkLanguageService {
66
68
  const lang = localStorage.getItem("spark-lang") ?? config.defaultLanguage;
67
69
  this.currentLang.set(lang);
68
70
  currentLanguage.set(lang);
71
+ this.writeCookie(lang);
72
+ }
73
+ writeCookie(lang) {
74
+ const document = this.document;
75
+ if (!document || !lang) return;
76
+ const secure = document.location?.protocol === "https:" ? "; Secure" : "";
77
+ document.cookie = `spark-lang=${encodeURIComponent(lang)}; Path=/; Max-Age=${LANGUAGE_COOKIE_MAX_AGE}; SameSite=Lax${secure}`;
69
78
  }
70
79
  async loadTranslations() {
71
80
  const t = await firstValueFrom(this.http.get(`${this.baseUrl}/translations`));
@@ -75,6 +84,7 @@ var SparkLanguageService = class SparkLanguageService {
75
84
  this.currentLang.set(lang);
76
85
  currentLanguage.set(lang);
77
86
  localStorage.setItem("spark-lang", lang);
87
+ this.writeCookie(lang);
78
88
  }
79
89
  resolve(ts) {
80
90
  if (!ts) return "";