@mintplayer/ng-spark 0.3.0 → 22.0.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 (34) hide show
  1. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +180 -0
  2. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -0
  3. package/fesm2022/mintplayer-ng-spark-icon.mjs +9 -6
  4. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  5. package/fesm2022/mintplayer-ng-spark-models.mjs +84 -1
  6. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  7. package/fesm2022/mintplayer-ng-spark-pipes.mjs +91 -74
  8. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  9. package/fesm2022/mintplayer-ng-spark-po-create.mjs +55 -22
  10. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  11. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +97 -92
  12. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  13. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +63 -11
  14. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  15. package/fesm2022/mintplayer-ng-spark-po-form.mjs +71 -36
  16. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  17. package/fesm2022/mintplayer-ng-spark-query-list.mjs +138 -126
  18. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  19. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +172 -18
  20. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  21. package/fesm2022/mintplayer-ng-spark-services.mjs +99 -50
  22. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  23. package/package.json +12 -7
  24. package/types/mintplayer-ng-spark-client-operations.d.ts +170 -0
  25. package/types/mintplayer-ng-spark-icon.d.ts +1 -1
  26. package/types/mintplayer-ng-spark-models.d.ts +54 -10
  27. package/types/mintplayer-ng-spark-pipes.d.ts +8 -0
  28. package/types/mintplayer-ng-spark-po-create.d.ts +2 -1
  29. package/types/mintplayer-ng-spark-po-detail.d.ts +14 -16
  30. package/types/mintplayer-ng-spark-po-edit.d.ts +4 -2
  31. package/types/mintplayer-ng-spark-po-form.d.ts +10 -9
  32. package/types/mintplayer-ng-spark-query-list.d.ts +26 -16
  33. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +33 -4
  34. package/types/mintplayer-ng-spark-services.d.ts +32 -6
@@ -0,0 +1,170 @@
1
+ import { PersistentObject } from '@mintplayer/ng-spark/models';
2
+ import * as i0 from '@angular/core';
3
+ import { InjectionToken, EnvironmentProviders } from '@angular/core';
4
+
5
+ declare enum NotificationKind {
6
+ Info = 0,
7
+ Success = 1,
8
+ Warning = 2,
9
+ Error = 3
10
+ }
11
+ interface NavigateOperation {
12
+ type: 'navigate';
13
+ objectTypeId?: string;
14
+ id?: string;
15
+ routeName?: string;
16
+ }
17
+ interface NotifyOperation {
18
+ type: 'notify';
19
+ message: string;
20
+ kind: NotificationKind;
21
+ durationMs?: number;
22
+ }
23
+ interface RefreshAttributeOperation {
24
+ type: 'refreshAttribute';
25
+ objectTypeId: string;
26
+ id: string;
27
+ attributeName: string;
28
+ value?: unknown;
29
+ }
30
+ interface RefreshQueryOperation {
31
+ type: 'refreshQuery';
32
+ queryId: string;
33
+ }
34
+ type DisableTarget = {
35
+ kind: 'persistentObject';
36
+ objectTypeId: string;
37
+ id: string;
38
+ } | {
39
+ kind: 'query';
40
+ queryId: string;
41
+ } | {
42
+ kind: 'currentResponse';
43
+ } | {
44
+ kind: 'session';
45
+ };
46
+ interface DisableActionOperation {
47
+ type: 'disableAction';
48
+ actionName: string;
49
+ target: DisableTarget;
50
+ }
51
+ interface RetryOperation {
52
+ type: 'retry';
53
+ step: number;
54
+ title: string;
55
+ options: string[];
56
+ defaultOption?: string | null;
57
+ persistentObject?: PersistentObject | null;
58
+ message?: string | null;
59
+ }
60
+ /**
61
+ * Discriminated union of known operation types, plus an open shape for unknown
62
+ * future operations. Handlers should narrow via the `type` discriminator before
63
+ * accessing fields specific to their operation type.
64
+ */
65
+ type ClientOperation = NavigateOperation | NotifyOperation | RefreshAttributeOperation | RefreshQueryOperation | DisableActionOperation | RetryOperation | {
66
+ type: string;
67
+ [key: string]: unknown;
68
+ };
69
+ /**
70
+ * Wire envelope returned by every action endpoint. `result` carries the primary
71
+ * payload (the PersistentObject for a Create, the QueryResult for an Execute,
72
+ * etc.); `operations` carries the side-effects the frontend dispatches.
73
+ */
74
+ interface ClientOperationEnvelope<T = unknown> {
75
+ result: T | null;
76
+ operations: ClientOperation[];
77
+ }
78
+
79
+ /**
80
+ * Routes received operations to registered handlers. Unknown operation types
81
+ * (no matching registration) are silently dropped — this is the forward-compat
82
+ * contract that lets new operation types ship server-side without coordinated
83
+ * client updates.
84
+ *
85
+ * Last-registered-wins on duplicate `type` values, matching standard
86
+ * Angular multi-provider override semantics.
87
+ *
88
+ * R2-H19 — security contract for handler authors:
89
+ * The dispatcher treats handler resolution as allow-list-by-type (unknown
90
+ * types drop). It does NOT validate the *content* of each operation. Handlers
91
+ * that act on URL-shaped fields (navigate, redirect, openWindow) MUST run
92
+ * the value through `sanitizeReturnUrl` from `@mintplayer/ng-spark-auth/models`
93
+ * (or an equivalent same-origin check) before acting on it. Otherwise a
94
+ * single attribute-echo XSS or a single mid-channel byte flip on a non-TLS
95
+ * path lets the server drive client navigation to an attacker host. The
96
+ * built-in `notify` handler renders via Angular interpolation (escaped) so
97
+ * it's safe to pass through, but anything more powerful must validate.
98
+ */
99
+ declare class SparkClientOperationDispatcher {
100
+ private readonly handlerMap;
101
+ constructor();
102
+ dispatch(operations: readonly ClientOperation[] | null | undefined): void;
103
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkClientOperationDispatcher, never>;
104
+ static ɵprov: i0.ɵɵInjectableDeclaration<SparkClientOperationDispatcher>;
105
+ }
106
+
107
+ /**
108
+ * A handler for a specific operation type. Receives the operation and
109
+ * executes the side-effect (e.g. show a toast, navigate, refresh a query).
110
+ * Handlers should `as`-narrow the operation to the type they registered for.
111
+ */
112
+ type ClientOperationHandler = (operation: ClientOperation) => void;
113
+ /**
114
+ * One entry in the multi-provider registration. Apps can register custom
115
+ * handlers alongside the built-in ones to extend the operation set with
116
+ * app-specific operation types.
117
+ */
118
+ interface ClientOperationHandlerRegistration {
119
+ type: string;
120
+ handler: ClientOperationHandler;
121
+ }
122
+ /**
123
+ * Multi-provider token. `provideSparkClientOperations()` registers the
124
+ * built-in handlers; apps can add their own with additional `multi: true`
125
+ * providers using this token.
126
+ */
127
+ declare const SPARK_CLIENT_OPERATION_HANDLERS: InjectionToken<readonly ClientOperationHandlerRegistration[]>;
128
+
129
+ interface SparkToast {
130
+ id: string;
131
+ message: string;
132
+ kind: NotificationKind;
133
+ durationMs: number;
134
+ }
135
+ /**
136
+ * Holds the active toasts as a signal. The `<spark-toast-container>` component
137
+ * renders them; the built-in `notify` operation handler pushes new toasts here.
138
+ *
139
+ * Auto-dismissal: each toast schedules its own removal after `durationMs`. Pass
140
+ * `0` to make a toast sticky (manual dismissal only).
141
+ */
142
+ declare class SparkNotificationService {
143
+ private readonly _toasts;
144
+ readonly toasts: i0.Signal<readonly SparkToast[]>;
145
+ show(message: string, kind?: NotificationKind, durationMs?: number): void;
146
+ dismiss(id: string): void;
147
+ clear(): void;
148
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkNotificationService, never>;
149
+ static ɵprov: i0.ɵɵInjectableDeclaration<SparkNotificationService>;
150
+ }
151
+
152
+ declare class SparkToastContainerComponent {
153
+ protected readonly notifications: SparkNotificationService;
154
+ protected readonly Kind: typeof NotificationKind;
155
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkToastContainerComponent, never>;
156
+ static ɵcmp: i0.ɵɵComponentDeclaration<SparkToastContainerComponent, "spark-toast-container", never, {}, {}, never, never, true, never>;
157
+ }
158
+
159
+ /**
160
+ * Registers the built-in client-operation handlers. Currently registers `notify`;
161
+ * additional types (`navigate`, `refreshQuery`, `refreshAttribute`, `disableAction`)
162
+ * land in subsequent commits. Apps add this once in their bootstrap providers.
163
+ *
164
+ * To register custom operation types alongside the built-ins, add additional
165
+ * `multi: true` providers using <see cref="SPARK_CLIENT_OPERATION_HANDLERS" />.
166
+ */
167
+ declare function provideSparkClientOperations(): EnvironmentProviders;
168
+
169
+ export { NotificationKind, SPARK_CLIENT_OPERATION_HANDLERS, SparkClientOperationDispatcher, SparkNotificationService, SparkToastContainerComponent, provideSparkClientOperations };
170
+ export type { ClientOperation, ClientOperationEnvelope, ClientOperationHandler, ClientOperationHandlerRegistration, DisableActionOperation, DisableTarget, NavigateOperation, NotifyOperation, RefreshAttributeOperation, RefreshQueryOperation, RetryOperation, SparkToast };
@@ -4,7 +4,7 @@ import * as _angular_core from '@angular/core';
4
4
  declare class SparkIconComponent {
5
5
  private registry;
6
6
  name: _angular_core.InputSignal<string>;
7
- iconHtml: _angular_core.Signal<_angular_platform_browser.SafeHtml>;
7
+ iconHtml: _angular_core.Signal<_angular_platform_browser.SafeHtml | undefined>;
8
8
  cssFallbackClass: _angular_core.Signal<string>;
9
9
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkIconComponent, never>;
10
10
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkIconComponent, "spark-icon", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
@@ -35,6 +35,14 @@ declare enum ShowedOn {
35
35
  */
36
36
  declare function hasShowedOnFlag(value: ShowedOn | string | undefined, flag: ShowedOn): boolean;
37
37
 
38
+ interface PersistentObject {
39
+ id: string;
40
+ name: string;
41
+ objectTypeId: string;
42
+ breadcrumb?: string;
43
+ attributes: PersistentObjectAttribute[];
44
+ }
45
+
38
46
  interface PersistentObjectAttribute {
39
47
  id: string;
40
48
  name: string;
@@ -61,14 +69,23 @@ interface PersistentObjectAttribute {
61
69
  renderer?: string;
62
70
  /** Options passed to the renderer component */
63
71
  rendererOptions?: Record<string, any>;
64
- }
65
-
66
- interface PersistentObject {
67
- id: string;
68
- name: string;
69
- objectTypeId: string;
70
- breadcrumb?: string;
71
- attributes: PersistentObjectAttribute[];
72
+ /**
73
+ * When `dataType === 'AsDetail'` and `isArray === false`: the single nested
74
+ * PersistentObject carrying the detail entity's values. `null` when the field is unset.
75
+ * Mirrors the server's PersistentObjectAttributeAsDetail.Object.
76
+ */
77
+ object?: PersistentObject | null;
78
+ /**
79
+ * When `dataType === 'AsDetail'` and `isArray === true`: the nested PO collection, one
80
+ * per array element. Mirrors PersistentObjectAttributeAsDetail.Objects.
81
+ */
82
+ objects?: PersistentObject[] | null;
83
+ /**
84
+ * CLR type name of the detail entity for AsDetail attributes (e.g. `HR.Entities.Address`).
85
+ * Emitted by the server so the frontend can look up the matching EntityType metadata
86
+ * without re-resolving through `asDetailType` on the schema definition.
87
+ */
88
+ asDetailType?: string;
72
89
  }
73
90
 
74
91
  interface EntityAttributeDefinition {
@@ -271,5 +288,32 @@ interface StreamingErrorMessage {
271
288
  }
272
289
  type StreamingMessage = StreamingSnapshotMessage | StreamingPatchMessage | StreamingErrorMessage;
273
290
 
274
- export { ELookupDisplayType, ShowedOn, currentLanguage, hasShowedOnFlag, resolveTranslation };
275
- export type { AttributeGroup, AttributeTab, CustomActionDefinition, EntityAttributeDefinition, EntityPermissions, EntityType, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, PersistentObjectAttribute, ProgramUnit, ProgramUnitGroup, ProgramUnitsConfiguration, QueryResult, RetryActionPayload, RetryActionResult, SparkQuery, SparkQueryRenderMode, SparkQuerySortColumn, StreamingErrorMessage, StreamingMessage, StreamingPatchItem, StreamingPatchMessage, StreamingSnapshotMessage, TranslatedString, ValidationError, ValidationErrorResponse, ValidationRule };
291
+ /**
292
+ * Resolves an `EntityType` by its CLR type name (e.g. `"HR.Entities.Address"`).
293
+ * Callers typically close over `sparkService.getEntityTypes()`'s cached list.
294
+ */
295
+ type EntityTypeResolver = (clrTypeName: string) => EntityType | undefined;
296
+ /**
297
+ * Flattens a nested `PersistentObject` into the plain `Record<string, any>` shape the
298
+ * form state uses throughout ng-spark. Primitive / reference attributes contribute their
299
+ * `value`; nested AsDetail attributes recurse — single becomes an inner dict, array
300
+ * becomes an array of inner dicts. Returns `{}` for `null` / `undefined` input.
301
+ *
302
+ * This is the ONE place that reads the server's new AsDetail wire shape and collapses it
303
+ * back to the flat dict the form components already handle.
304
+ */
305
+ declare function nestedPoToDict(po: PersistentObject | null | undefined): Record<string, any>;
306
+ /**
307
+ * Builds a nested `PersistentObject` from a flat dict against the schema in
308
+ * <paramref name="entityType"/>. Used when the form is about to save — AsDetail attributes
309
+ * are no longer sent as flat dicts in `attribute.value`; the server now requires
310
+ * `attribute.object` / `attribute.objects` with fully scaffolded nested POs.
311
+ *
312
+ * `resolve` walks through AsDetail types registered elsewhere (usually the full
313
+ * `getEntityTypes()` list, keyed by CLR type name). Nested AsDetail inside AsDetail is
314
+ * handled recursively.
315
+ */
316
+ declare function dictToNestedPo(dict: Record<string, any> | null | undefined, entityType: EntityType, resolve: EntityTypeResolver): PersistentObject;
317
+
318
+ export { ELookupDisplayType, ShowedOn, currentLanguage, dictToNestedPo, hasShowedOnFlag, nestedPoToDict, resolveTranslation };
319
+ export type { AttributeGroup, AttributeTab, CustomActionDefinition, EntityAttributeDefinition, EntityPermissions, EntityType, EntityTypeResolver, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, PersistentObjectAttribute, ProgramUnit, ProgramUnitGroup, ProgramUnitsConfiguration, QueryResult, RetryActionPayload, RetryActionResult, SparkQuery, SparkQueryRenderMode, SparkQuerySortColumn, StreamingErrorMessage, StreamingMessage, StreamingPatchItem, StreamingPatchMessage, StreamingSnapshotMessage, TranslatedString, ValidationError, ValidationErrorResponse, ValidationRule };
@@ -134,6 +134,14 @@ declare class IconNamePipe implements PipeTransform {
134
134
  static ɵpipe: i0.ɵɵPipeDeclaration<IconNamePipe, "iconName", true>;
135
135
  }
136
136
 
137
+ /**
138
+ * Resolves an attribute to a list of flat row dicts for the detail-page table.
139
+ * AsDetail array attributes carry their data as nested PersistentObjects in
140
+ * <c>attr.objects</c>, not <c>attr.value</c> — the server stopped putting flat
141
+ * dicts in <c>value</c> when AsDetail moved to its dedicated wire shape. Without
142
+ * this branch, the detail page rendered AsDetail arrays as empty tables even
143
+ * when the edit page (which reads <c>attr.objects</c> directly) showed rows.
144
+ */
137
145
  declare class ArrayValuePipe implements PipeTransform {
138
146
  transform(attrName: string, item: PersistentObject | null): Record<string, any>[];
139
147
  static ɵfac: i0.ɵɵFactoryDeclaration<ArrayValuePipe, never>;
@@ -10,11 +10,12 @@ declare class SparkPoCreateComponent {
10
10
  saved: _angular_core.OutputEmitterRef<PersistentObject>;
11
11
  cancelled: _angular_core.OutputEmitterRef<void>;
12
12
  colors: typeof Color;
13
- entityType: _angular_core.WritableSignal<EntityType>;
13
+ entityType: _angular_core.WritableSignal<EntityType | null>;
14
14
  type: _angular_core.WritableSignal<string>;
15
15
  formData: _angular_core.WritableSignal<Record<string, any>>;
16
16
  validationErrors: _angular_core.WritableSignal<ValidationError[]>;
17
17
  isSaving: _angular_core.WritableSignal<boolean>;
18
+ private allEntityTypes;
18
19
  generalErrors: _angular_core.Signal<ValidationError[]>;
19
20
  constructor();
20
21
  private onParamsChange;
@@ -1,23 +1,22 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { TemplateRef, Type } from '@angular/core';
3
3
  import { Color } from '@mintplayer/ng-bootstrap';
4
+ import { SparkLanguageService } from '@mintplayer/ng-spark/services';
4
5
  import { PersistentObject, EntityType, CustomActionDefinition, LookupReference, EntityAttributeDefinition, AttributeTab, AttributeGroup, SparkQuery } from '@mintplayer/ng-spark/models';
5
- import { DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';
6
- import { VirtualDatatableDataSource } from '@mintplayer/ng-bootstrap/virtual-datatable';
7
- import { PaginationResponse } from '@mintplayer/pagination';
6
+ import { DatatableSettings, BsDatatableFetch } from '@mintplayer/ng-bootstrap/datatable';
8
7
 
9
8
  declare class SparkPoDetailComponent {
10
9
  private readonly route;
11
10
  private readonly router;
12
11
  private readonly sparkService;
13
- private readonly lang;
12
+ protected readonly lang: SparkLanguageService;
14
13
  private readonly rendererRegistry;
15
14
  showCustomActions: _angular_core.InputSignal<boolean>;
16
- extraActionsTemplate: _angular_core.InputSignal<TemplateRef<void>>;
15
+ extraActionsTemplate: _angular_core.InputSignal<TemplateRef<void> | null>;
17
16
  extraContentTemplate: _angular_core.InputSignal<TemplateRef<{
18
17
  $implicit: PersistentObject;
19
18
  entityType: EntityType;
20
- }>>;
19
+ }> | null>;
21
20
  edited: _angular_core.OutputEmitterRef<void>;
22
21
  deleted: _angular_core.OutputEmitterRef<void>;
23
22
  customActionExecuted: _angular_core.OutputEmitterRef<{
@@ -25,10 +24,10 @@ declare class SparkPoDetailComponent {
25
24
  item: PersistentObject;
26
25
  }>;
27
26
  colors: typeof Color;
28
- errorMessage: _angular_core.WritableSignal<string>;
29
- entityType: _angular_core.WritableSignal<EntityType>;
27
+ errorMessage: _angular_core.WritableSignal<string | null>;
28
+ entityType: _angular_core.WritableSignal<EntityType | null>;
30
29
  allEntityTypes: _angular_core.WritableSignal<EntityType[]>;
31
- item: _angular_core.WritableSignal<PersistentObject>;
30
+ item: _angular_core.WritableSignal<PersistentObject | null>;
32
31
  lookupReferenceOptions: _angular_core.WritableSignal<Record<string, LookupReference>>;
33
32
  asDetailTypes: _angular_core.WritableSignal<Record<string, EntityType>>;
34
33
  asDetailReferenceOptions: _angular_core.WritableSignal<Record<string, Record<string, PersistentObject[]>>>;
@@ -63,21 +62,20 @@ declare class SparkSubQueryComponent {
63
62
  queryId: _angular_core.InputSignal<string>;
64
63
  parentId: _angular_core.InputSignal<string>;
65
64
  parentType: _angular_core.InputSignal<string>;
66
- query: _angular_core.WritableSignal<SparkQuery>;
67
- entityType: _angular_core.WritableSignal<EntityType>;
65
+ query: _angular_core.WritableSignal<SparkQuery | null>;
66
+ entityType: _angular_core.WritableSignal<EntityType | null>;
68
67
  allEntityTypes: _angular_core.WritableSignal<EntityType[]>;
69
- paginationData: _angular_core.WritableSignal<PaginationResponse<PersistentObject>>;
68
+ resultCount: _angular_core.WritableSignal<number | null>;
70
69
  lookupReferenceOptions: _angular_core.WritableSignal<Record<string, LookupReference>>;
71
70
  loading: _angular_core.WritableSignal<boolean>;
72
71
  canRead: _angular_core.WritableSignal<boolean>;
73
72
  settings: _angular_core.WritableSignal<DatatableSettings>;
74
- virtualDataSource: _angular_core.WritableSignal<VirtualDatatableDataSource<PersistentObject>>;
75
- virtualSettings: _angular_core.WritableSignal<DatatableSettings>;
73
+ fetchFn: _angular_core.WritableSignal<BsDatatableFetch<PersistentObject> | null>;
74
+ isVirtualScrolling: _angular_core.Signal<boolean>;
76
75
  visibleAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
77
76
  constructor();
78
77
  private loadData;
79
- private loadPage;
80
- onSettingsChange(): void;
78
+ private makeFetch;
81
79
  private loadLookupReferenceOptions;
82
80
  getColumnRendererComponent(attr: EntityAttributeDefinition): Type<any> | null;
83
81
  getColumnRendererInputs(item: PersistentObject, attr: EntityAttributeDefinition): Record<string, any>;
@@ -10,17 +10,19 @@ declare class SparkPoEditComponent {
10
10
  saved: _angular_core.OutputEmitterRef<PersistentObject>;
11
11
  cancelled: _angular_core.OutputEmitterRef<void>;
12
12
  colors: typeof Color;
13
- entityType: _angular_core.WritableSignal<EntityType>;
14
- item: _angular_core.WritableSignal<PersistentObject>;
13
+ entityType: _angular_core.WritableSignal<EntityType | null>;
14
+ item: _angular_core.WritableSignal<PersistentObject | null>;
15
15
  type: string;
16
16
  id: string;
17
17
  formData: _angular_core.WritableSignal<Record<string, any>>;
18
18
  validationErrors: _angular_core.WritableSignal<ValidationError[]>;
19
19
  isSaving: _angular_core.WritableSignal<boolean>;
20
+ private allEntityTypes;
20
21
  generalErrors: _angular_core.Signal<ValidationError[]>;
21
22
  constructor();
22
23
  private onParamsChange;
23
24
  initFormData(): void;
25
+ private resolveEntityType;
24
26
  getEditableAttributes(): _mintplayer_ng_spark_models.EntityAttributeDefinition[];
25
27
  onSave(): Promise<void>;
26
28
  onCancel(): void;
@@ -9,33 +9,33 @@ declare class SparkPoFormComponent {
9
9
  private readonly sparkService;
10
10
  private readonly translations;
11
11
  private readonly rendererRegistry;
12
- entityType: _angular_core.InputSignal<EntityType>;
12
+ entityType: _angular_core.InputSignal<EntityType | null>;
13
13
  formData: _angular_core.ModelSignal<Record<string, any>>;
14
14
  validationErrors: _angular_core.InputSignal<ValidationError[]>;
15
15
  showButtons: _angular_core.InputSignal<boolean>;
16
16
  isSaving: _angular_core.InputSignal<boolean>;
17
- parentId: _angular_core.InputSignal<string>;
18
- parentType: _angular_core.InputSignal<string>;
17
+ parentId: _angular_core.InputSignal<string | undefined>;
18
+ parentType: _angular_core.InputSignal<string | undefined>;
19
19
  save: _angular_core.OutputEmitterRef<void>;
20
20
  cancel: _angular_core.OutputEmitterRef<void>;
21
21
  colors: typeof Color;
22
22
  referenceOptions: _angular_core.WritableSignal<Record<string, PersistentObject[]>>;
23
23
  asDetailTypes: _angular_core.WritableSignal<Record<string, EntityType>>;
24
24
  lookupReferenceOptions: _angular_core.WritableSignal<Record<string, LookupReference>>;
25
- editingAsDetailAttr: _angular_core.WritableSignal<EntityAttributeDefinition>;
25
+ editingAsDetailAttr: _angular_core.WritableSignal<EntityAttributeDefinition | null>;
26
26
  asDetailFormData: _angular_core.WritableSignal<Record<string, any>>;
27
27
  showAsDetailModal: _angular_core.WritableSignal<boolean>;
28
- editingArrayIndex: _angular_core.WritableSignal<number>;
28
+ editingArrayIndex: _angular_core.WritableSignal<number | null>;
29
29
  asDetailPermissions: _angular_core.WritableSignal<Record<string, EntityPermissions>>;
30
30
  asDetailReferenceOptions: _angular_core.WritableSignal<Record<string, Record<string, PersistentObject[]>>>;
31
- editingReferenceAttr: _angular_core.WritableSignal<EntityAttributeDefinition>;
31
+ editingReferenceAttr: _angular_core.WritableSignal<EntityAttributeDefinition | null>;
32
32
  showReferenceModal: _angular_core.WritableSignal<boolean>;
33
33
  referenceModalItems: _angular_core.WritableSignal<PersistentObject[]>;
34
- referenceModalEntityType: _angular_core.WritableSignal<EntityType>;
35
- referenceModalPagination: _angular_core.WritableSignal<PaginationResponse<PersistentObject>>;
34
+ referenceModalEntityType: _angular_core.WritableSignal<EntityType | null>;
35
+ referenceModalPagination: _angular_core.WritableSignal<PaginationResponse<PersistentObject> | undefined>;
36
36
  referenceModalSettings: _angular_core.WritableSignal<DatatableSettings>;
37
37
  referenceSearchTerm: string;
38
- editingLookupAttr: _angular_core.WritableSignal<EntityAttributeDefinition>;
38
+ editingLookupAttr: _angular_core.WritableSignal<EntityAttributeDefinition | null>;
39
39
  showLookupModal: _angular_core.WritableSignal<boolean>;
40
40
  lookupModalItems: _angular_core.WritableSignal<LookupReferenceValue[]>;
41
41
  lookupSearchTerm: _angular_core.WritableSignal<string>;
@@ -47,6 +47,7 @@ declare class SparkPoFormComponent {
47
47
  groupsForTab(tab: AttributeTab): AttributeGroup[];
48
48
  attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[];
49
49
  referenceVisibleAttributes: _angular_core.Signal<EntityAttributeDefinition[]>;
50
+ referenceModalRows: _angular_core.Signal<PersistentObject[]>;
50
51
  filteredLookupItems: _angular_core.Signal<LookupReferenceValue[]>;
51
52
  constructor();
52
53
  private toRecord;
@@ -1,46 +1,56 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { TemplateRef, Type } from '@angular/core';
3
3
  import { Color } from '@mintplayer/ng-bootstrap';
4
- import { DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';
5
- import { VirtualDatatableDataSource } from '@mintplayer/ng-bootstrap/virtual-datatable';
6
- import { PaginationResponse } from '@mintplayer/pagination';
7
- import { PersistentObject, SparkQuery, EntityType, LookupReference, EntityAttributeDefinition } from '@mintplayer/ng-spark/models';
4
+ import { BsDatatableFetch, DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';
5
+ import { SparkLanguageService } from '@mintplayer/ng-spark/services';
6
+ import { PersistentObject, CustomActionDefinition, SparkQuery, EntityType, LookupReference, EntityAttributeDefinition } from '@mintplayer/ng-spark/models';
8
7
 
9
8
  declare class SparkQueryListComponent {
10
9
  private readonly route;
11
10
  private readonly router;
12
11
  private readonly sparkService;
13
12
  private readonly streamingService;
13
+ protected readonly lang: SparkLanguageService;
14
14
  private readonly rendererRegistry;
15
15
  private readonly destroyRef;
16
- extraActionsTemplate: _angular_core.InputSignal<TemplateRef<void>>;
16
+ extraActionsTemplate: _angular_core.InputSignal<TemplateRef<void> | null>;
17
+ showCustomActions: _angular_core.InputSignal<boolean>;
17
18
  rowClicked: _angular_core.OutputEmitterRef<PersistentObject>;
18
19
  createClicked: _angular_core.OutputEmitterRef<void>;
20
+ customActionExecuted: _angular_core.OutputEmitterRef<{
21
+ action: CustomActionDefinition;
22
+ }>;
19
23
  colors: typeof Color;
20
- errorMessage: _angular_core.WritableSignal<string>;
21
- query: _angular_core.WritableSignal<SparkQuery>;
22
- entityType: _angular_core.WritableSignal<EntityType>;
24
+ errorMessage: _angular_core.WritableSignal<string | null>;
25
+ query: _angular_core.WritableSignal<SparkQuery | null>;
26
+ entityType: _angular_core.WritableSignal<EntityType | null>;
23
27
  allEntityTypes: _angular_core.WritableSignal<EntityType[]>;
24
28
  lookupReferenceOptions: _angular_core.WritableSignal<Record<string, LookupReference>>;
25
- paginationData: _angular_core.WritableSignal<PaginationResponse<PersistentObject>>;
29
+ resultCount: _angular_core.WritableSignal<number | null>;
26
30
  searchTerm: string;
27
31
  canRead: _angular_core.WritableSignal<boolean>;
28
32
  canCreate: _angular_core.WritableSignal<boolean>;
33
+ customActions: _angular_core.WritableSignal<CustomActionDefinition[]>;
29
34
  isStreaming: _angular_core.WritableSignal<boolean>;
30
35
  private streamingSub;
31
36
  private allItems;
32
- private filteredItems;
37
+ streamItems: _angular_core.WritableSignal<PersistentObject[]>;
38
+ fetchFn: _angular_core.WritableSignal<BsDatatableFetch<PersistentObject> | null>;
33
39
  settings: _angular_core.WritableSignal<DatatableSettings>;
34
- virtualDataSource: _angular_core.WritableSignal<VirtualDatatableDataSource<PersistentObject>>;
35
- virtualSettings: _angular_core.WritableSignal<DatatableSettings>;
36
40
  constructor();
37
41
  private onParamsChange;
42
+ onCustomAction(action: CustomActionDefinition): Promise<void>;
38
43
  private resolveEntityTypeForQuery;
39
44
  private extractSourceName;
40
45
  private singularize;
41
- private initVirtualDataSource;
42
- loadItems(): Promise<void>;
43
- onSettingsChange(): void;
46
+ /**
47
+ * Builds the server-side fetch callback the datatable invokes per page/sort.
48
+ * Reads `searchTerm` live, so a settings change (or a new fetchFn identity)
49
+ * refetches with the current search term.
50
+ */
51
+ private makeFetch;
52
+ /** Force a refetch (e.g. after a custom action) without changing page/sort. */
53
+ private refresh;
44
54
  onSearchChange(): void;
45
55
  clearSearch(): void;
46
56
  isVirtualScrolling: _angular_core.Signal<boolean>;
@@ -54,7 +64,7 @@ declare class SparkQueryListComponent {
54
64
  private handleStreamingMessage;
55
65
  private applyFilter;
56
66
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkQueryListComponent, never>;
57
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkQueryListComponent, "spark-query-list", never, { "extraActionsTemplate": { "alias": "extraActionsTemplate"; "required": false; "isSignal": true; }; }, { "rowClicked": "rowClicked"; "createClicked": "createClicked"; }, never, never, true, never>;
67
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkQueryListComponent, "spark-query-list", never, { "extraActionsTemplate": { "alias": "extraActionsTemplate"; "required": false; "isSignal": true; }; "showCustomActions": { "alias": "showCustomActions"; "required": false; "isSignal": true; }; }, { "rowClicked": "rowClicked"; "createClicked": "createClicked"; "customActionExecuted": "customActionExecuted"; }, never, never, true, never>;
58
68
  }
59
69
 
60
70
  export { SparkQueryListComponent };
@@ -1,14 +1,43 @@
1
- import * as i0 from '@angular/core';
1
+ import * as _angular_core from '@angular/core';
2
2
  import { Color } from '@mintplayer/ng-bootstrap';
3
3
  import { RetryActionService } from '@mintplayer/ng-spark/services';
4
+ import { EntityType } from '@mintplayer/ng-spark/models';
4
5
 
6
+ /**
7
+ * Renders a retry-action popup. Before PRD §3 this component rendered title / message /
8
+ * option buttons only and silently forwarded the incoming <c>persistentObject</c> back to
9
+ * the server on submit — meaning any <c>Retry.Action(..., persistentObject)</c> flow had
10
+ * no UI to actually edit the PO. This component now embeds the shared PO form so every
11
+ * scalar / Reference / AsDetail attribute on the scaffolded Virtual PO is a real form
12
+ * field, and the values the user fills in flow back to the server via
13
+ * <c>RetryResult.PersistentObject</c>.
14
+ */
5
15
  declare class SparkRetryActionModalComponent {
6
16
  protected readonly retryActionService: RetryActionService;
17
+ private readonly sparkService;
7
18
  colors: typeof Color;
8
- isOpen: i0.Signal<boolean>;
19
+ isOpen: _angular_core.Signal<boolean>;
20
+ /**
21
+ * EntityType definition for the incoming PO — fetched lazily via SparkService so the
22
+ * form knows which attributes are editable, their labels, rules, renderers, etc.
23
+ * `null` when the payload has no persistentObject or its objectTypeId doesn't match
24
+ * any registered entity type (renders the modal as a simple option picker).
25
+ */
26
+ entityType: _angular_core.WritableSignal<EntityType | null>;
27
+ formData: _angular_core.WritableSignal<Record<string, any>>;
28
+ private allEntityTypes;
29
+ constructor();
30
+ private seedForm;
9
31
  onOption(option: string): void;
10
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkRetryActionModalComponent, never>;
11
- static ɵcmp: i0.ɵɵComponentDeclaration<SparkRetryActionModalComponent, "spark-retry-action-modal", never, {}, {}, never, never, true, never>;
32
+ /**
33
+ * Builds the PO the server sees under <c>Retry.Result.PersistentObject</c>. If the
34
+ * form resolved an EntityType, rebuild from the schema + formData (identical to the
35
+ * po-edit save path — AsDetail recursion included). Otherwise forward the incoming
36
+ * PO unmodified so pre-§3 flows without editable attributes keep working.
37
+ */
38
+ private populatedPersistentObject;
39
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkRetryActionModalComponent, never>;
40
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkRetryActionModalComponent, "spark-retry-action-modal", never, {}, {}, never, never, true, never>;
12
41
  }
13
42
 
14
43
  export { SparkRetryActionModalComponent };
@@ -6,7 +6,7 @@ import { SafeHtml } from '@angular/platform-browser';
6
6
 
7
7
  declare class RetryActionService {
8
8
  private resolveRetry;
9
- payload: i0.WritableSignal<RetryActionPayload>;
9
+ payload: i0.WritableSignal<RetryActionPayload | null>;
10
10
  show(payload: RetryActionPayload): Promise<RetryActionResult>;
11
11
  respond(result: RetryActionResult): void;
12
12
  static ɵfac: i0.ɵɵFactoryDeclaration<RetryActionService, never>;
@@ -46,6 +46,7 @@ declare class SparkService {
46
46
  private readonly baseUrl;
47
47
  private readonly http;
48
48
  private readonly retryActionService;
49
+ private readonly dispatcher;
49
50
  getEntityTypes(): Promise<EntityType[]>;
50
51
  getEntityType(id: string): Promise<EntityType>;
51
52
  getEntityTypeByClrType(clrType: string): Promise<EntityType | undefined>;
@@ -78,19 +79,44 @@ declare class SparkService {
78
79
  addLookupReferenceValue(name: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
79
80
  updateLookupReferenceValue(name: string, key: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
80
81
  deleteLookupReferenceValue(name: string, key: string): Promise<void>;
81
- private postWithRetry;
82
- private putWithRetry;
83
- private deleteWithRetry;
84
- private handleRetryError;
82
+ private postWithEnvelope;
83
+ private putWithEnvelope;
84
+ private deleteWithEnvelope;
85
+ private sendWithEnvelope;
86
+ private handleEnvelopeRetryError;
85
87
  static ɵfac: i0.ɵɵFactoryDeclaration<SparkService, never>;
86
88
  static ɵprov: i0.ɵɵInjectableDeclaration<SparkService>;
87
89
  }
88
90
 
91
+ /**
92
+ * R2-M12: SVG icon registry. The registry no longer accepts a raw string —
93
+ * apps must pre-sanitize via `DomSanitizer.bypassSecurityTrustHtml` (or
94
+ * `parseSvg` to validate) before calling `register`. Treating the parameter
95
+ * as `SafeHtml` puts the explicit bypass decision in the caller's hands so a
96
+ * developer wiring server-supplied SVG (per-tenant branding fetched from a
97
+ * JSON endpoint) doesn't accidentally trust a `<svg><script>...</script>`
98
+ * payload.
99
+ *
100
+ * The built-in icons baked into the package are still bypass-trusted internally
101
+ * because they're known-safe at build time.
102
+ */
89
103
  declare class SparkIconRegistry {
90
104
  private sanitizer;
91
105
  private icons;
92
106
  constructor();
93
- register(name: string, svg: string): void;
107
+ /**
108
+ * Registers a pre-sanitized SVG icon. Callers MUST validate or explicitly
109
+ * trust the SVG before passing — for example:
110
+ *
111
+ * ```ts
112
+ * const safe = sanitizer.bypassSecurityTrustHtml(svgFromBuildTimeConstant);
113
+ * registry.register('app-logo', safe);
114
+ * ```
115
+ *
116
+ * For server-supplied SVG, prefer a strict allow-list parser (no `<script>`,
117
+ * no `on*` attributes, no `href="javascript:..."`) before trusting.
118
+ */
119
+ register(name: string, svg: SafeHtml): void;
94
120
  get(name: string): SafeHtml | undefined;
95
121
  has(name: string): boolean;
96
122
  static ɵfac: i0.ɵɵFactoryDeclaration<SparkIconRegistry, never>;