@mintplayer/ng-spark 22.23.0 → 22.25.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 (71) hide show
  1. package/README.md +196 -0
  2. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +100 -47
  3. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs.map +1 -1
  4. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +310 -329
  5. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-column-filter.mjs +175 -135
  7. package/fesm2022/mintplayer-ng-spark-column-filter.mjs.map +1 -1
  8. package/fesm2022/mintplayer-ng-spark-contributions.mjs +721 -0
  9. package/fesm2022/mintplayer-ng-spark-contributions.mjs.map +1 -0
  10. package/fesm2022/mintplayer-ng-spark-grid.mjs +2385 -1118
  11. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  12. package/fesm2022/mintplayer-ng-spark-history.mjs +598 -0
  13. package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -0
  14. package/fesm2022/mintplayer-ng-spark-icon.mjs +64 -27
  15. package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
  16. package/fesm2022/mintplayer-ng-spark-models.mjs +405 -762
  17. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  18. package/fesm2022/mintplayer-ng-spark-moderation.mjs +1212 -0
  19. package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -0
  20. package/fesm2022/mintplayer-ng-spark-panels.mjs +60 -0
  21. package/fesm2022/mintplayer-ng-spark-panels.mjs.map +1 -0
  22. package/fesm2022/mintplayer-ng-spark-pipes.mjs +1158 -765
  23. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  24. package/fesm2022/mintplayer-ng-spark-po-create.mjs +273 -169
  25. package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
  26. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +795 -410
  27. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  28. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +1196 -280
  29. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  30. package/fesm2022/mintplayer-ng-spark-po-form.mjs +2244 -1224
  31. package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
  32. package/fesm2022/mintplayer-ng-spark-query-list.mjs +571 -303
  33. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  34. package/fesm2022/mintplayer-ng-spark-renderers.mjs +44 -55
  35. package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
  36. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +216 -177
  37. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-routes.mjs +24 -28
  39. package/fesm2022/mintplayer-ng-spark-routes.mjs.map +1 -1
  40. package/fesm2022/mintplayer-ng-spark-services.mjs +625 -640
  41. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  42. package/fesm2022/mintplayer-ng-spark-shell.mjs +1031 -397
  43. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  44. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +294 -0
  45. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -0
  46. package/fesm2022/mintplayer-ng-spark.mjs +14 -48
  47. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  48. package/package.json +23 -2
  49. package/types/mintplayer-ng-spark-attribute-description.d.ts +25 -18
  50. package/types/mintplayer-ng-spark-client-operations.d.ts +139 -160
  51. package/types/mintplayer-ng-spark-column-filter.d.ts +73 -55
  52. package/types/mintplayer-ng-spark-contributions.d.ts +231 -0
  53. package/types/mintplayer-ng-spark-grid.d.ts +917 -448
  54. package/types/mintplayer-ng-spark-history.d.ts +216 -0
  55. package/types/mintplayer-ng-spark-icon.d.ts +15 -13
  56. package/types/mintplayer-ng-spark-models.d.ts +681 -573
  57. package/types/mintplayer-ng-spark-moderation.d.ts +402 -0
  58. package/types/mintplayer-ng-spark-panels.d.ts +143 -0
  59. package/types/mintplayer-ng-spark-pipes.d.ts +131 -149
  60. package/types/mintplayer-ng-spark-po-create.d.ts +46 -37
  61. package/types/mintplayer-ng-spark-po-detail.d.ts +180 -117
  62. package/types/mintplayer-ng-spark-po-edit.d.ts +367 -64
  63. package/types/mintplayer-ng-spark-po-form.d.ts +558 -373
  64. package/types/mintplayer-ng-spark-query-list.d.ts +143 -82
  65. package/types/mintplayer-ng-spark-renderers.d.ts +121 -85
  66. package/types/mintplayer-ng-spark-retry-action-modal.d.ts +31 -34
  67. package/types/mintplayer-ng-spark-routes.d.ts +7 -11
  68. package/types/mintplayer-ng-spark-services.d.ts +377 -261
  69. package/types/mintplayer-ng-spark-shell.d.ts +228 -132
  70. package/types/mintplayer-ng-spark-soft-delete.d.ts +90 -0
  71. package/types/mintplayer-ng-spark.d.ts +19 -12
@@ -1,202 +1,251 @@
1
- import * as i0 from '@angular/core';
2
- import { RetryActionPayload, RetryActionResult, TranslatedString, StreamingMessage, QueryColumnFilter, EntityType, EntityPermissions, SparkQuery, QueryResult, DistinctValuesResult, ProgramUnitsConfiguration, PersistentObject, CustomActionDefinition, LookupReferenceListItem, LookupReference, LookupReferenceValue } from '@mintplayer/ng-spark/models';
3
- import { Observable } from 'rxjs';
4
- import { SortColumn } from '@mintplayer/pagination';
5
- import { SafeHtml } from '@angular/platform-browser';
6
- import { HttpInterceptorFn, HttpFeature, HttpFeatureKind } from '@angular/common/http';
7
-
8
- declare class RetryActionService {
9
- private resolveRetry;
10
- payload: i0.WritableSignal<RetryActionPayload | null>;
11
- show(payload: RetryActionPayload): Promise<RetryActionResult>;
12
- respond(result: RetryActionResult): void;
13
- static ɵfac: i0.ɵɵFactoryDeclaration<RetryActionService, never>;
14
- static ɵprov: i0.ɵɵInjectableDeclaration<RetryActionService>;
1
+ import { CustomActionDefinition, DistinctValuesResult, EntityPermissions, EntityType, LookupReference, LookupReferenceListItem, LookupReferenceValue, PersistentObject, ProgramUnitsConfiguration, QueryColumnFilter, QueryResult, RetryActionPayload, RetryActionResult, SparkDeletedFilter, SparkQuery, StreamingMessage, TranslatedString } from "@mintplayer/ng-spark/models";
2
+ import * as i0 from "@angular/core";
3
+ import { Observable } from "rxjs";
4
+ import { SortColumn } from "@mintplayer/pagination";
5
+ import { SafeHtml } from "@angular/platform-browser";
6
+ import { HttpFeature, HttpFeatureKind, HttpInterceptorFn } from "@angular/common/http";
7
+ export declare class RetryActionService {
8
+ private resolveRetry;
9
+ payload: import("@angular/core").WritableSignal<RetryActionPayload | null>;
10
+ show(payload: RetryActionPayload): Promise<RetryActionResult>;
11
+ respond(result: RetryActionResult): void;
12
+ static ɵfac: i0.ɵɵFactoryDeclaration<RetryActionService, never>;
13
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
15
14
  }
16
-
17
- declare class SparkLanguageService {
18
- private readonly http;
19
- private readonly config;
20
- private readonly baseUrl;
21
- private readonly currentLang;
22
- private readonly translationsMap;
23
- readonly language: i0.Signal<string>;
24
- readonly languages: i0.WritableSignal<Record<string, TranslatedString>>;
25
- constructor();
26
- private loadCulture;
27
- private loadTranslations;
28
- setLanguage(lang: string): void;
29
- resolve(ts: TranslatedString | undefined): string;
30
- t(key: string): string;
31
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkLanguageService, never>;
32
- static ɵprov: i0.ɵɵInjectableDeclaration<SparkLanguageService>;
15
+ export declare class SparkLanguageService {
16
+ private readonly http;
17
+ private readonly config;
18
+ private readonly baseUrl;
19
+ private readonly currentLang;
20
+ private readonly translationsMap;
21
+ readonly language: import("@angular/core").Signal<string>;
22
+ readonly languages: import("@angular/core").WritableSignal<Record<string, TranslatedString>>;
23
+ constructor();
24
+ private loadCulture;
25
+ private loadTranslations;
26
+ setLanguage(lang: string): void;
27
+ resolve(ts: TranslatedString | undefined): string;
28
+ t(key: string): string;
29
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkLanguageService, never>;
30
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
33
31
  }
34
-
35
- declare class SparkStreamingService {
36
- private readonly config;
37
- private readonly baseUrl;
38
- private readonly ngZone;
39
- connectToStreamingQuery(queryId: string): Observable<StreamingMessage>;
40
- private buildWebSocketUrl;
41
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkStreamingService, never>;
42
- static ɵprov: i0.ɵɵInjectableDeclaration<SparkStreamingService>;
32
+ export declare class SparkStreamingService {
33
+ private readonly config;
34
+ private readonly baseUrl;
35
+ private readonly ngZone;
36
+ connectToStreamingQuery(queryId: string): Observable<StreamingMessage>;
37
+ private buildWebSocketUrl;
38
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkStreamingService, never>;
39
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
43
40
  }
44
-
45
41
  /**
46
42
  * Context for {@link SparkService.newObject}. Everything is optional: a standalone New sends an
47
43
  * empty body, and an AsDetail row on a parent that has never been saved sends no `parentId` —
48
44
  * which is the honest answer, not a gap. The server hands the hook a null parent rather than
49
45
  * trusting the client's copy of one.
50
46
  */
51
- interface NewObjectOptions {
52
- /** Name of the parent's AsDetail attribute the row is for; absent for a standalone New. */
53
- asDetailAttribute?: string;
54
- /** The parent's entity type — required whenever `asDetailAttribute` is set. */
55
- parentType?: string;
56
- /** The parent's id, absent while the parent is itself unsaved. */
57
- parentId?: string;
58
- /** Free-form arguments, e.g. which variant a New menu chose. */
59
- parameters?: Record<string, string>;
47
+ export interface NewObjectOptions {
48
+ /** Name of the parent's AsDetail attribute the row is for; absent for a standalone New. */
49
+ asDetailAttribute?: string;
50
+ /** The parent's entity type — required whenever `asDetailAttribute` is set. */
51
+ parentType?: string;
52
+ /** The parent's id, absent while the parent is itself unsaved. */
53
+ parentId?: string;
54
+ /** Free-form arguments, e.g. which variant a New menu chose. */
55
+ parameters?: Record<string, string>;
56
+ /**
57
+ * The sub-query New was started from (#460, D19), with `parentType` and `parentId` and no
58
+ * `asDetailAttribute`. The server loads the parent, checks the parent's type declares this
59
+ * sub-query, and runs `OnNewAsync` with it — whose base fills the reference to the parent.
60
+ */
61
+ queryId?: string;
62
+ }
63
+ /** Where a bulk delete was started from; see {@link SparkService.deleteMany}. */
64
+ export interface DeleteManyOptions {
65
+ /** The query the rows were selected in, for the server's `OnDisableActionsAsync`. */
66
+ queryId?: string;
67
+ /** The sub-query's container, when deleting from a sub-query. */
68
+ parentId?: string;
69
+ parentType?: string;
60
70
  }
61
71
  /** Context for {@link SparkService.deleteRow}. Every field is required — see `DeleteRow.cs`. */
62
- interface DeleteRowOptions {
63
- asDetailAttribute: string;
64
- parentType: string;
65
- parentId: string;
66
- /** The row's `__sparkRowKey`, as it arrived from the server. */
67
- rowKey: string;
68
- parameters?: Record<string, string>;
72
+ export interface DeleteRowOptions {
73
+ asDetailAttribute: string;
74
+ parentType: string;
75
+ parentId: string;
76
+ /** The row's `__sparkRowKey`, as it arrived from the server. */
77
+ rowKey: string;
78
+ parameters?: Record<string, string>;
69
79
  }
70
- declare class SparkService {
71
- #private;
72
- private readonly config;
73
- private readonly baseUrl;
74
- private readonly http;
75
- private readonly retryActionService;
76
- /**
77
- * How many prompts one request will answer before giving up. Matches the .NET client's
78
- * `SparkClient.MaxRetryDepth`, and for the same reason: this is a termination bound, not a
79
- * capacity one. Far above any real conversation (Fleet's longest is two) and far below anything
80
- * that could hide a spin.
81
- */
82
- private static readonly MAX_RETRY_DEPTH;
83
- private readonly dispatcher;
84
- getEntityTypes(): Promise<EntityType[]>;
85
- getEntityType(id: string): Promise<EntityType>;
86
- getEntityTypeByClrType(clrType: string): Promise<EntityType | undefined>;
87
- getPermissions(entityTypeId: string): Promise<EntityPermissions>;
88
- getQueries(): Promise<SparkQuery[]>;
89
- getQuery(id: string): Promise<SparkQuery>;
90
- /**
91
- * Resolves a query by name, fetching the catalogue at most once per service instance.
92
- *
93
- * Without the cache this costs a full `GET /spark/queries` per call, and a form with several
94
- * reference attributes resolves them in parallel — so opening one page fetched the whole query
95
- * catalogue N times over. The catalogue is per-caller and effectively static for a page's
96
- * lifetime; the service is scoped to the app, so a permission change lands on the next load.
97
- */
98
- getQueryByName(name: string): Promise<SparkQuery | undefined>;
99
- executeQuery(queryId: string, options?: ExecuteQueryOptions): Promise<QueryResult>;
100
- /**
101
- * The distinct values behind one column's filter panel (#431).
102
- *
103
- * `columns` carries the OTHER columns' current filters, so the values returned are the ones still
104
- * reachable — offering a value that empties the grid the moment it is picked is worse than
105
- * omitting it.
106
- *
107
- * An empty result means either "nothing matches" or "you may not enumerate this column". The two
108
- * are deliberately indistinguishable, so there is nothing for a caller to branch on.
109
- */
110
- getDistinctValues(queryId: string, column: string, options?: {
111
- search?: string;
112
- columns?: QueryColumnFilter[];
113
- parentId?: string;
114
- parentType?: string;
115
- }): Promise<DistinctValuesResult>;
116
- /**
117
- * Runs a query identified by name.
118
- *
119
- * It used to forward only the parent, dropping skip/take/search — so every reference picker and
120
- * AsDetail reference column silently saw the server's default of 50 candidates, with no indication
121
- * that the list was cut. A picker that omits the option you are looking for, and says nothing, is
122
- * worse than one that fails.
123
- */
124
- executeQueryByName(queryName: string, options?: ExecuteQueryOptions): Promise<QueryResult>;
125
- getProgramUnits(): Promise<ProgramUnitsConfiguration>;
126
- get(type: string, id: string): Promise<PersistentObject>;
127
- create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
128
- update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
129
- /**
130
- * Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.
131
- *
132
- * Writes nothing, but goes through the envelope like every other mutating call: a refresh may
133
- * legitimately emit notifications, and may open the retry-action prompt.
134
- *
135
- * `triggeredBy` is the attribute's name. For a trigger inside an AsDetail row it is the same
136
- * path form the inline validation errors use — `Jobs[2].ProfessionId`.
137
- */
138
- refresh(type: string, data: Partial<PersistentObject>, triggeredBy: string): Promise<PersistentObject>;
139
- /**
140
- * Asks the server to construct a new object of `type`, so `OnNewAsync` can default it.
141
- *
142
- * Writes nothing — the object comes back unsaved, and for an AsDetail row the parent still owns
143
- * the save. Only called for a row type whose `serverSideRowLifecycle` is on; every other type
144
- * keeps building its blank row locally, which is why switching the flag off costs no request.
145
- *
146
- * ⚠️ The row comes back **keyed**: the server constructs the CLR instance, so the row-key field
147
- * initializer runs and `po.id` carries the key. Flattening it with `nestedPoToDict` therefore
148
- * populates `__sparkRowKey`, and the row is matchable on save from the moment it is added — the
149
- * whole reason this round-trip is worth a request.
150
- */
151
- newObject(type: string, options?: NewObjectOptions): Promise<PersistentObject>;
152
- /**
153
- * Asks whether a stored row may leave its parent's AsDetail collection.
154
- *
155
- * Deletes nothing: a resolved promise means the caller may splice the row out of the collection
156
- * it is editing, and the removal is persisted with the parent. A hook that refuses rejects with a
157
- * 400 whose `error.error.result.errors` carries the readable reason, exactly like a save.
158
- *
159
- * A POST — as everything is now. It used to be worth explaining: the framework's real delete route
160
- * was a catch-all that would have swallowed a sibling DELETE. The catch-all is gone with the rest
161
- * of the route variables.
162
- */
163
- deleteRow(type: string, options: DeleteRowOptions): Promise<void>;
164
- delete(type: string, id: string): Promise<void>;
165
- getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]>;
166
- /**
167
- * @param parent The object of THIS type the action is operating on — the detail-page invocation.
168
- * @param selectedItemIds Row ids from a query. Ids, never row objects: a row is a projection.
169
- * @param queryParent When invoked from a sub-query, the object whose detail page it was rendered
170
- * on — a DIFFERENT type (the cars listed on a company's page are Cars, the page is a Company).
171
- * Sent as id + type, exactly as the query endpoint names it, and resolved server-side under its
172
- * own type with its own Read gate.
173
- * @param queryId The query the selection came from, so the server can re-run it narrowed to those
174
- * ids and hand the action the rows the grid actually had -- index-computed columns included.
175
- */
176
- executeCustomAction(objectTypeId: string, actionName: string, parent?: PersistentObject, selectedItemIds?: string[], queryParent?: {
177
- id: string;
178
- type: string;
179
- }, queryId?: string): Promise<void>;
180
- getLookupReferences(): Promise<LookupReferenceListItem[]>;
181
- getLookupReference(name: string): Promise<LookupReference>;
182
- addLookupReferenceValue(name: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
183
- updateLookupReferenceValue(name: string, key: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
184
- deleteLookupReferenceValue(name: string, key: string): Promise<void>;
185
- private postWithEnvelope;
80
+ export declare class SparkService {
81
+ #private;
82
+ private readonly config;
83
+ private readonly baseUrl;
84
+ private readonly http;
85
+ private readonly retryActionService;
86
+ /**
87
+ * How many prompts one request will answer before giving up. Matches the .NET client's
88
+ * `SparkClient.MaxRetryDepth`, and for the same reason: this is a termination bound, not a
89
+ * capacity one. Far above any real conversation (Fleet's longest is two) and far below anything
90
+ * that could hide a spin.
91
+ */
92
+ private static readonly MAX_RETRY_DEPTH;
93
+ private readonly dispatcher;
94
+ getEntityTypes(): Promise<EntityType[]>;
95
+ getEntityType(id: string): Promise<EntityType>;
96
+ getEntityTypeByClrType(clrType: string): Promise<EntityType | undefined>;
97
+ getPermissions(entityTypeId: string): Promise<EntityPermissions>;
98
+ getQueries(): Promise<SparkQuery[]>;
99
+ getQuery(id: string): Promise<SparkQuery>;
100
+ /**
101
+ * Resolves a query by name, fetching the catalogue at most once per service instance.
102
+ *
103
+ * Without the cache this costs a full `GET /spark/queries` per call, and a form with several
104
+ * reference attributes resolves them in parallel — so opening one page fetched the whole query
105
+ * catalogue N times over. The catalogue is per-caller and effectively static for a page's
106
+ * lifetime; the service is scoped to the app, so a permission change lands on the next load.
107
+ */
108
+ getQueryByName(name: string): Promise<SparkQuery | undefined>;
109
+ executeQuery(queryId: string, options?: ExecuteQueryOptions): Promise<QueryResult>;
110
+ /**
111
+ * The distinct values behind one column's filter panel (#431).
112
+ *
113
+ * `columns` carries the OTHER columns' current filters, so the values returned are the ones still
114
+ * reachable — offering a value that empties the grid the moment it is picked is worse than
115
+ * omitting it.
116
+ *
117
+ * An empty result means either "nothing matches" or "you may not enumerate this column". The two
118
+ * are deliberately indistinguishable, so there is nothing for a caller to branch on.
119
+ */
120
+ getDistinctValues(queryId: string, column: string, options?: {
121
+ /** Narrows the listed values themselves (the panel's own box). */
122
+ search?: string;
186
123
  /**
187
- * A read: posts the request, and returns the response **as-is** rather than unwrapping an
188
- * envelope.
189
- *
190
- * The read endpoints moved from GET to POST so their hooks could prompt, but their success
191
- * responses are still bare — a `PersistentObject`, a `QueryResult`. Only the 449 is enveloped,
192
- * because a retry has nowhere else to live. So this shares the retry loop with the mutating calls
193
- * and nothing else.
124
+ * The grid's search term, what `executeQuery` sends as `search` (#460 M15): the values come only
125
+ * from the rows it matches, so a searched grid's panel lists what the grid shows.
194
126
  */
195
- private sendRead;
196
- private sendWithEnvelope;
197
- private handleEnvelopeRetryError;
198
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkService, never>;
199
- static ɵprov: i0.ɵɵInjectableDeclaration<SparkService>;
127
+ querySearch?: string;
128
+ columns?: QueryColumnFilter[];
129
+ parentId?: string;
130
+ parentType?: string;
131
+ /** The same soft-deletion mode as the grid the panel belongs to (#460). */
132
+ deleted?: SparkDeletedFilter;
133
+ /** The same parent mode as the grid the panel belongs to (#460). */
134
+ parentDeleted?: SparkDeletedFilter;
135
+ }): Promise<DistinctValuesResult>;
136
+ /**
137
+ * Runs a query identified by name.
138
+ *
139
+ * It used to forward only the parent, dropping skip/take/search — so every reference picker and
140
+ * AsDetail reference column silently saw the server's default of 50 candidates, with no indication
141
+ * that the list was cut. A picker that omits the option you are looking for, and says nothing, is
142
+ * worse than one that fails.
143
+ */
144
+ executeQueryByName(queryName: string, options?: ExecuteQueryOptions): Promise<QueryResult>;
145
+ getProgramUnits(): Promise<ProgramUnitsConfiguration>;
146
+ /**
147
+ * Loads one object. `options.deleted` (#460) lets a `ViewDeleted` holder open a soft-deleted row
148
+ * from the recycle bin (`'include'` or `'only'`); without the right the server ignores it and a
149
+ * deleted row stays a 404. Omitted = exclude.
150
+ */
151
+ get(type: string, id: string, options?: {
152
+ deleted?: SparkDeletedFilter;
153
+ }): Promise<PersistentObject>;
154
+ create(type: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
155
+ update(type: string, id: string, data: Partial<PersistentObject>): Promise<PersistentObject>;
156
+ /**
157
+ * Asks the server to reshape an in-progress object after `triggeredBy`'s value changed.
158
+ *
159
+ * Writes nothing, but goes through the envelope like every other mutating call: a refresh may
160
+ * legitimately emit notifications, and may open the retry-action prompt.
161
+ *
162
+ * `triggeredBy` is the attribute's name. For a trigger inside an AsDetail row it is the same
163
+ * path form the inline validation errors use — `Jobs[2].ProfessionId`.
164
+ */
165
+ refresh(type: string, data: Partial<PersistentObject>, triggeredBy: string): Promise<PersistentObject>;
166
+ /**
167
+ * Asks the server to construct a new object of `type`, so `OnNewAsync` can default it.
168
+ *
169
+ * Writes nothing — the object comes back unsaved, and for an AsDetail row the parent still owns
170
+ * the save. Called by the create page for every New (#460, D19: with `parentId`/`parentType`/
171
+ * `queryId` when started from a sub-query, whose base hook fills the parent reference), and for an
172
+ * AsDetail row only when its type's `serverSideRowLifecycle` is on; every other row type keeps
173
+ * building its blank row locally, which is why switching the flag off costs no request.
174
+ *
175
+ * ⚠️ The row comes back **keyed**: the server constructs the CLR instance, so the row-key field
176
+ * initializer runs and `po.id` carries the key. Flattening it with `nestedPoToDict` therefore
177
+ * populates `__sparkRowKey`, and the row is matchable on save from the moment it is added — the
178
+ * whole reason this round-trip is worth a request.
179
+ */
180
+ newObject(type: string, options?: NewObjectOptions): Promise<PersistentObject>;
181
+ /**
182
+ * Asks whether a stored row may leave its parent's AsDetail collection.
183
+ *
184
+ * Deletes nothing: a resolved promise means the caller may splice the row out of the collection
185
+ * it is editing, and the removal is persisted with the parent. A hook that refuses rejects with a
186
+ * 400 whose `error.error.result.errors` carries the readable reason, exactly like a save.
187
+ *
188
+ * A POST — as everything is now. It used to be worth explaining: the framework's real delete route
189
+ * was a catch-all that would have swallowed a sibling DELETE. The catch-all is gone with the rest
190
+ * of the route variables.
191
+ */
192
+ deleteRow(type: string, options: DeleteRowOptions): Promise<void>;
193
+ delete(type: string, id: string): Promise<void>;
194
+ /**
195
+ * Deletes several rows of one type in one request — the default Delete action on a selection
196
+ * (`POST /spark/po/delete-many`, #460 D18). All or nothing: a 404 means some row is missing or not
197
+ * yours to delete, a 403 that the server's `OnDisableActionsAsync` withholds Delete on the query or
198
+ * on one of the rows, a 400 that the selection breaks the rule or the 200-row cap.
199
+ */
200
+ deleteMany(type: string, ids: string[], options?: DeleteManyOptions): Promise<void>;
201
+ getCustomActions(objectTypeId: string): Promise<CustomActionDefinition[]>;
202
+ /**
203
+ * @param parent The object of THIS type the action is operating on — the detail-page invocation.
204
+ * @param selectedItemIds Row ids from a query. Ids, never row objects: a row is a projection.
205
+ * @param queryParent When invoked from a sub-query, the object whose detail page it was rendered
206
+ * on — a DIFFERENT type (the cars listed on a company's page are Cars, the page is a Company).
207
+ * Sent as id + type, exactly as the query endpoint names it, and resolved server-side under its
208
+ * own type with its own Read gate.
209
+ * @param queryId The query the selection came from, so the server can re-run it narrowed to those
210
+ * ids and hand the action the rows the grid actually had -- index-computed columns included.
211
+ * @returns What the action handed to `CustomActionArgs.SetResult` (#460, T5) -- the envelope's
212
+ * `result` -- or `undefined` when it set nothing. The value is the action author's to shape: it
213
+ * bypasses the server's redaction. A 403 means the action is disabled for this object, query or
214
+ * selection (the server's `OnDisableActionsAsync` said so), distinct from a 404 for a row the
215
+ * caller cannot see.
216
+ */
217
+ executeCustomAction<T = unknown>(objectTypeId: string, actionName: string, parent?: PersistentObject, selectedItemIds?: string[], queryParent?: {
218
+ id: string;
219
+ type: string;
220
+ }, queryId?: string): Promise<T | undefined>;
221
+ getLookupReferences(): Promise<LookupReferenceListItem[]>;
222
+ getLookupReference(name: string): Promise<LookupReference>;
223
+ addLookupReferenceValue(name: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
224
+ updateLookupReferenceValue(name: string, key: string, value: LookupReferenceValue): Promise<LookupReferenceValue>;
225
+ deleteLookupReferenceValue(name: string, key: string): Promise<void>;
226
+ /**
227
+ * Posts to an add-on package's endpoint under the Spark base URL (`/spark` + `path`) and unwraps
228
+ * the `ClientOperationEnvelope` exactly like the built-in mutations: operations are dispatched and
229
+ * a 449 retry opens the retry modal. For the ng-spark add-on entry points (`soft-delete`,
230
+ * `history`, `moderation`) whose server packages answer through core's envelope helpers
231
+ * (`SparkAddOnEndpoints`, #460).
232
+ */
233
+ postEnvelope<T>(path: string, body: Record<string, unknown>): Promise<T>;
234
+ private postWithEnvelope;
235
+ /**
236
+ * A read: posts the request, and returns the response **as-is** rather than unwrapping an
237
+ * envelope.
238
+ *
239
+ * The read endpoints moved from GET to POST so their hooks could prompt, but their success
240
+ * responses are still bare — a `PersistentObject`, a `QueryResult`. Only the 449 is enveloped,
241
+ * because a retry has nowhere else to live. So this shares the retry loop with the mutating calls
242
+ * and nothing else.
243
+ */
244
+ private sendRead;
245
+ private sendWithEnvelope;
246
+ private handleEnvelopeRetryError;
247
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkService, never>;
248
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
200
249
  }
201
250
  /**
202
251
  * The options every query execution takes.
@@ -205,17 +254,28 @@ declare class SparkService {
205
254
  * options as separate inline literals a field added to one silently failed to forward from the
206
255
  * other — which is how skip/take/search were once dropped for every reference picker.
207
256
  */
208
- interface ExecuteQueryOptions {
209
- sortColumns?: SortColumn[];
210
- parentId?: string;
211
- parentType?: string;
212
- skip?: number;
213
- take?: number;
214
- search?: string;
215
- /** Per-column value filters (#431). Columns AND together; values within a column OR. */
216
- columns?: QueryColumnFilter[];
257
+ export interface ExecuteQueryOptions {
258
+ sortColumns?: SortColumn[];
259
+ parentId?: string;
260
+ parentType?: string;
261
+ skip?: number;
262
+ take?: number;
263
+ search?: string;
264
+ /** Per-column value filters (#431). Columns AND together; values within a column OR. */
265
+ columns?: QueryColumnFilter[];
266
+ /**
267
+ * Soft-deletion mode (#460): `exclude` (default), `include`, `only`. Sent only when set; the server
268
+ * honours a widening only for callers holding `ViewDeleted` on the type.
269
+ */
270
+ deleted?: SparkDeletedFilter;
271
+ /**
272
+ * The mode the PARENT (`parentId`/`parentType`) is resolved under (#460) — `include` for a sub-query
273
+ * on a deleted object's page, opened from the recycle bin. Independent of `deleted`, which stays the
274
+ * rows' filter. Honoured only for callers holding `ViewDeleted` on the parent's type; anyone else
275
+ * gets the same 404 a missing parent gives.
276
+ */
277
+ parentDeleted?: SparkDeletedFilter;
217
278
  }
218
-
219
279
  /**
220
280
  * R2-M12: SVG icon registry. The registry no longer accepts a raw string —
221
281
  * apps must pre-sanitize via `DomSanitizer.bypassSecurityTrustHtml` (or
@@ -228,29 +288,28 @@ interface ExecuteQueryOptions {
228
288
  * The built-in icons baked into the package are still bypass-trusted internally
229
289
  * because they're known-safe at build time.
230
290
  */
231
- declare class SparkIconRegistry {
232
- private sanitizer;
233
- private icons;
234
- constructor();
235
- /**
236
- * Registers a pre-sanitized SVG icon. Callers MUST validate or explicitly
237
- * trust the SVG before passing — for example:
238
- *
239
- * ```ts
240
- * const safe = sanitizer.bypassSecurityTrustHtml(svgFromBuildTimeConstant);
241
- * registry.register('app-logo', safe);
242
- * ```
243
- *
244
- * For server-supplied SVG, prefer a strict allow-list parser (no `<script>`,
245
- * no `on*` attributes, no `href="javascript:..."`) before trusting.
246
- */
247
- register(name: string, svg: SafeHtml): void;
248
- get(name: string): SafeHtml | undefined;
249
- has(name: string): boolean;
250
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkIconRegistry, never>;
251
- static ɵprov: i0.ɵɵInjectableDeclaration<SparkIconRegistry>;
291
+ export declare class SparkIconRegistry {
292
+ private sanitizer;
293
+ private icons;
294
+ constructor();
295
+ /**
296
+ * Registers a pre-sanitized SVG icon. Callers MUST validate or explicitly
297
+ * trust the SVG before passing — for example:
298
+ *
299
+ * ```ts
300
+ * const safe = sanitizer.bypassSecurityTrustHtml(svgFromBuildTimeConstant);
301
+ * registry.register('app-logo', safe);
302
+ * ```
303
+ *
304
+ * For server-supplied SVG, prefer a strict allow-list parser (no `<script>`,
305
+ * no `on*` attributes, no `href="javascript:..."`) before trusting.
306
+ */
307
+ register(name: string, svg: SafeHtml): void;
308
+ get(name: string): SafeHtml | undefined;
309
+ has(name: string): boolean;
310
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkIconRegistry, never>;
311
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
252
312
  }
253
-
254
313
  /**
255
314
  * A query's custom actions, reachable **without the grid that usually renders them**.
256
315
  *
@@ -274,44 +333,58 @@ declare class SparkIconRegistry {
274
333
  * It also does not cache. Actions depend on the caller, and a memo keyed by type id would survive
275
334
  * a sign-out.
276
335
  */
277
- declare class SparkQueryActionsService {
278
- private readonly sparkService;
279
- /**
280
- * The custom actions of the query named by id or alias, already filtered to those that belong on
281
- * a query surface.
282
- *
283
- * Returns an empty list — rather than throwing — when the query resolves to no entity type,
284
- * because a caller rendering a toolbar wants "no buttons", not a broken page.
285
- */
286
- actionsFor(queryIdOrAlias: string): Promise<CustomActionDefinition[]>;
287
- /**
288
- * Runs one of those actions. `selectedItemIds` are row ids, exactly as the grid posts them —
289
- * the server re-materializes each one through the same load path a detail page uses, so an id
290
- * from anywhere is treated as caller input rather than as a verified row.
291
- */
292
- execute(queryIdOrAlias: string, actionName: string, options?: {
293
- parent?: PersistentObject;
294
- selectedItemIds?: string[];
295
- /** For a sub-query: the object whose detail page it was rendered on. See SparkService. */
296
- queryParent?: {
297
- id: string;
298
- type: string;
299
- };
300
- }): Promise<void>;
301
- /**
302
- * The query and the entity type its rows are mapped against — the two-step resolution both
303
- * methods need, kept in one place so they cannot disagree about which type an action runs on.
304
- */
305
- private contextFor;
306
- static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryActionsService, never>;
307
- static ɵprov: i0.ɵɵInjectableDeclaration<SparkQueryActionsService>;
336
+ export declare class SparkQueryActionsService {
337
+ private readonly sparkService;
338
+ /**
339
+ * The custom actions of the query named by id or alias, already filtered to those that belong on
340
+ * a query surface.
341
+ *
342
+ * Returns an empty list — rather than throwing — when the query resolves to no entity type,
343
+ * because a caller rendering a toolbar wants "no buttons", not a broken page.
344
+ */
345
+ actionsFor(queryIdOrAlias: string): Promise<CustomActionDefinition[]>;
346
+ /**
347
+ * Runs one of those actions. `selectedItemIds` are row ids, exactly as the grid posts them —
348
+ * the server re-materializes each one through the same load path a detail page uses, so an id
349
+ * from anywhere is treated as caller input rather than as a verified row.
350
+ */
351
+ execute(queryIdOrAlias: string, actionName: string, options?: {
352
+ parent?: PersistentObject;
353
+ selectedItemIds?: string[];
354
+ /** For a sub-query: the object whose detail page it was rendered on. See SparkService. */
355
+ queryParent?: {
356
+ id: string;
357
+ type: string;
358
+ };
359
+ }): Promise<void>;
360
+ /**
361
+ * The query and the entity type its rows are mapped against — the two-step resolution both
362
+ * methods need, kept in one place so they cannot disagree about which type an action runs on.
363
+ */
364
+ private contextFor;
365
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkQueryActionsService, never>;
366
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
308
367
  }
309
-
310
368
  /**
311
369
  * The header carrying the viewer's IANA timezone id, e.g. `Europe/Brussels`.
312
370
  * Must stay in lockstep with `RequestTimeZoneResolver.HeaderName` on the server.
313
371
  */
314
- declare const SPARK_TIMEZONE_HEADER = "X-Spark-Timezone";
372
+ export declare const SPARK_TIMEZONE_HEADER = "X-Spark-Timezone";
373
+ /**
374
+ * The default name of the cookie carrying the same id, for requests the browser makes without any
375
+ * script running first (a server-side render, a link opened from a mail). Must stay in lockstep
376
+ * with `SparkTimeZoneOptions.DefaultCookieName` (`Spark:TimeZone:CookieName`) on the server.
377
+ */
378
+ export declare const SPARK_TIMEZONE_COOKIE = "spark-timezone";
379
+ /** Options for {@link withSparkTimezone}. */
380
+ export interface SparkTimezoneOptions {
381
+ /**
382
+ * The cookie to write the viewer's zone into; `false` writes none (the header is still sent).
383
+ * Defaults to {@link SPARK_TIMEZONE_COOKIE}. Use the same name as the server's
384
+ * `Spark:TimeZone:CookieName`.
385
+ */
386
+ cookieName?: string | false;
387
+ }
315
388
  /**
316
389
  * Returns the browser's IANA zone id, or `null` when the environment cannot name one.
317
390
  *
@@ -319,23 +392,66 @@ declare const SPARK_TIMEZONE_HEADER = "X-Spark-Timezone";
319
392
  * so a server asked to render "next Tuesday at 09:00" from an offset captured today gets it wrong
320
393
  * across a DST boundary. A zone id carries the rules, not one sample of them.
321
394
  */
322
- declare function resolveBrowserTimeZone(): string | null;
395
+ export declare function resolveBrowserTimeZone(): string | null;
323
396
  /**
324
- * Attaches {@link SPARK_TIMEZONE_HEADER} to same-origin requests.
397
+ * Builds the interceptor for {@link withSparkTimezone}. Exported for apps that compose their own
398
+ * `withInterceptors([...])`.
325
399
  *
326
400
  * **This does not participate in reading or writing timestamps.** A `DateTimeOffset` means an
327
- * instant, and the browser converts in both directions on those paths. The header exists for work
328
- * the server starts on its own — a scheduled export, a notification email — where there is no
329
- * browser in the loop to do the converting but the output still has to read as local time.
401
+ * instant, and the browser converts in both directions on those paths. The header and cookie exist
402
+ * for work the server does on its own — a scheduled export, a notification email, a server-side
403
+ * render — where there is no browser in the loop to do the converting but the output still has to
404
+ * read as local time.
405
+ *
406
+ * ⚠️ **Nothing happens on the server platform.** During a server-side render `Intl` names the Node
407
+ * process's zone (UTC in a container), and a header carrying it would win over the viewer's cookie
408
+ * on the server (spike S-TZ3, #460).
330
409
  */
331
- declare const sparkTimezoneInterceptor: HttpInterceptorFn;
410
+ export declare function createSparkTimezoneInterceptor(options?: SparkTimezoneOptions): HttpInterceptorFn;
332
411
  /**
333
- * `provideHttpClient(...withSparkTimezone())` — registers {@link sparkTimezoneInterceptor}.
412
+ * Attaches {@link SPARK_TIMEZONE_HEADER} to same-origin requests and keeps the
413
+ * {@link SPARK_TIMEZONE_COOKIE} cookie current — with the default options. See
414
+ * {@link createSparkTimezoneInterceptor}.
415
+ */
416
+ export declare const sparkTimezoneInterceptor: HttpInterceptorFn;
417
+ /**
418
+ * `provideHttpClient(...withSparkTimezone())` — registers the timezone interceptor.
334
419
  *
335
420
  * Spreadable alongside `withSparkAuth()`:
336
421
  * `provideHttpClient(...withSparkAuth(), ...withSparkTimezone())`.
422
+ *
423
+ * The cookie is written on the first request the browser app makes, so the very first server-side
424
+ * render of a first visit has no cookie and renders in UTC.
425
+ */
426
+ export declare function withSparkTimezone(options?: SparkTimezoneOptions): HttpFeature<HttpFeatureKind>[];
427
+ /** The `history.state` key a list row link stores the list's own URL under. */
428
+ export declare const SPARK_RETURN_URL_STATE_KEY = "sparkReturnUrl";
429
+ /**
430
+ * Where a detail page goes back to after its row stops existing — a Delete, or a Purge from the
431
+ * recycle bin (#460).
432
+ *
433
+ * Neither "the start page" nor "the previous history entry" is the right answer: the first loses the
434
+ * user's place, the second may be the deleted row's edit page, or another site. The right answer is
435
+ * the list the row was opened from, so a list's row link records that list's URL in the navigation
436
+ * state ({@link stateForRowLink}); a detail page reached otherwise (a bookmark, a reference link)
437
+ * falls back to the row type's own list — its program-unit query, else any query over the type.
337
438
  */
338
- declare function withSparkTimezone(): HttpFeature<HttpFeatureKind>[];
339
-
340
- export { RetryActionService, SPARK_TIMEZONE_HEADER, SparkIconRegistry, SparkLanguageService, SparkQueryActionsService, SparkService, SparkStreamingService, resolveBrowserTimeZone, sparkTimezoneInterceptor, withSparkTimezone };
341
- export type { DeleteRowOptions, ExecuteQueryOptions, NewObjectOptions };
439
+ export declare class SparkReturnNavigationService {
440
+ private readonly router;
441
+ private readonly spark;
442
+ /** Navigation state for a row link: the current URL (the list, with its `?deleted=` mode). */
443
+ stateForRowLink(): Record<string, string>;
444
+ /** The list URL the current detail page was opened from, when a row link recorded one. */
445
+ recordedReturnUrl(): string | null;
446
+ /**
447
+ * Navigates to the list the row came from, else to the type's list (`?deleted=only` kept when
448
+ * `deletedView`), else to the start page. `replaceUrl`: the gone row's page is not worth a Back.
449
+ */
450
+ returnToList(entityTypeName: string | null | undefined, options?: {
451
+ deletedView?: boolean;
452
+ }): Promise<void>;
453
+ /** `['/query', alias]` of the type's list: a menu (program-unit) query first, else any query. */
454
+ listRouteFor(entityTypeName: string): Promise<unknown[] | null>;
455
+ static ɵfac: i0.ɵɵFactoryDeclaration<SparkReturnNavigationService, never>;
456
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
457
+ }