@mintplayer/ng-spark 22.31.0 → 22.33.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 (75) hide show
  1. package/README.md +101 -1
  2. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +3 -3
  3. package/fesm2022/mintplayer-ng-spark-auth-account.mjs +18 -18
  4. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +16 -8
  5. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +3 -3
  7. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +6 -6
  8. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +3 -3
  9. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +3 -3
  10. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +3 -3
  11. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +3 -3
  12. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +3 -3
  13. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +3 -3
  14. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +3 -3
  15. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +3 -3
  16. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +24 -24
  17. package/fesm2022/mintplayer-ng-spark-column-filter.mjs +3 -3
  18. package/fesm2022/mintplayer-ng-spark-contributions.mjs +9 -9
  19. package/fesm2022/mintplayer-ng-spark-grid.mjs +220 -49
  20. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  21. package/fesm2022/mintplayer-ng-spark-history.mjs +9 -9
  22. package/fesm2022/mintplayer-ng-spark-icon.mjs +3 -3
  23. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +3 -3
  24. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +9 -9
  25. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +6 -6
  26. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +3 -3
  27. package/fesm2022/mintplayer-ng-spark-models.mjs +44 -1
  28. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  29. package/fesm2022/mintplayer-ng-spark-moderation.mjs +21 -21
  30. package/fesm2022/mintplayer-ng-spark-notifications.mjs +687 -0
  31. package/fesm2022/mintplayer-ng-spark-notifications.mjs.map +1 -0
  32. package/fesm2022/mintplayer-ng-spark-organizations-accept.mjs +179 -0
  33. package/fesm2022/mintplayer-ng-spark-organizations-accept.mjs.map +1 -0
  34. package/fesm2022/mintplayer-ng-spark-organizations.mjs +202 -0
  35. package/fesm2022/mintplayer-ng-spark-organizations.mjs.map +1 -0
  36. package/fesm2022/mintplayer-ng-spark-pipes.mjs +125 -85
  37. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-po-create.mjs +3 -3
  39. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +90 -26
  40. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  41. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +84 -12
  42. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  43. package/fesm2022/mintplayer-ng-spark-po-form.mjs +9 -9
  44. package/fesm2022/mintplayer-ng-spark-pwa.mjs +74 -6
  45. package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -1
  46. package/fesm2022/mintplayer-ng-spark-query-list.mjs +10 -109
  47. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  48. package/fesm2022/mintplayer-ng-spark-renderers.mjs +3 -3
  49. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +3 -3
  50. package/fesm2022/mintplayer-ng-spark-score-renderers.mjs +801 -0
  51. package/fesm2022/mintplayer-ng-spark-score-renderers.mjs.map +1 -0
  52. package/fesm2022/mintplayer-ng-spark-services.mjs +424 -72
  53. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  54. package/fesm2022/mintplayer-ng-spark-share-page.mjs +334 -0
  55. package/fesm2022/mintplayer-ng-spark-share-page.mjs.map +1 -0
  56. package/fesm2022/mintplayer-ng-spark-share.mjs +10 -0
  57. package/fesm2022/mintplayer-ng-spark-share.mjs.map +1 -0
  58. package/fesm2022/mintplayer-ng-spark-shell.mjs +38 -36
  59. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  60. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +9 -9
  61. package/package.json +25 -1
  62. package/types/mintplayer-ng-spark-grid.d.ts +77 -26
  63. package/types/mintplayer-ng-spark-models.d.ts +150 -17
  64. package/types/mintplayer-ng-spark-notifications.d.ts +188 -0
  65. package/types/mintplayer-ng-spark-organizations-accept.d.ts +40 -0
  66. package/types/mintplayer-ng-spark-organizations.d.ts +72 -0
  67. package/types/mintplayer-ng-spark-pipes.d.ts +10 -0
  68. package/types/mintplayer-ng-spark-po-detail.d.ts +26 -0
  69. package/types/mintplayer-ng-spark-po-edit.d.ts +37 -0
  70. package/types/mintplayer-ng-spark-pwa.d.ts +48 -2
  71. package/types/mintplayer-ng-spark-query-list.d.ts +8 -23
  72. package/types/mintplayer-ng-spark-score-renderers.d.ts +340 -0
  73. package/types/mintplayer-ng-spark-services.d.ts +146 -6
  74. package/types/mintplayer-ng-spark-share-page.d.ts +67 -0
  75. package/types/mintplayer-ng-spark-share.d.ts +26 -0
@@ -1,9 +1,9 @@
1
1
  import * as i0 from "@angular/core";
2
- import { Injectable, NgZone, PLATFORM_ID, inject, signal } from "@angular/core";
2
+ import { DestroyRef, Injectable, Injector, NgZone, PLATFORM_ID, assertInInjectionContext, effect, inject, signal, untracked } from "@angular/core";
3
3
  import { DOCUMENT, isPlatformBrowser } from "@angular/common";
4
4
  import { HttpClient, withInterceptors } from "@angular/common/http";
5
- import { Observable, firstValueFrom } from "rxjs";
6
- import { SPARK_RETRY_CANCEL, currentLanguage, filterQueryActions } from "@mintplayer/ng-spark/models";
5
+ import { BehaviorSubject, Observable, firstValueFrom, isObservable, of } from "rxjs";
6
+ import { LIVE_CLOSE_MESSAGE_TOO_LARGE, LIVE_CLOSE_REAUTHENTICATE, LIVE_CLOSE_TOO_MANY_SOCKETS, SPARK_RETRY_CANCEL, applyLivePoPatch, applyLiveQueryPatch, currentLanguage, filterQueryActions } from "@mintplayer/ng-spark/models";
7
7
  import { SPARK_CONFIG } from "@mintplayer/ng-spark";
8
8
  import { SparkClientMethodRegistry, SparkClientOperationDispatcher } from "@mintplayer/ng-spark/client-operations";
9
9
  import { DomSanitizer } from "@angular/platform-browser";
@@ -24,7 +24,7 @@ var RetryActionService = class RetryActionService {
24
24
  }
25
25
  static ɵfac = i0.ɵɵngDeclareFactory({
26
26
  minVersion: "12.0.0",
27
- version: "22.2.0",
27
+ version: "22.2.2",
28
28
  ngImport: i0,
29
29
  type: RetryActionService,
30
30
  deps: [],
@@ -32,7 +32,7 @@ var RetryActionService = class RetryActionService {
32
32
  });
33
33
  static ɵprov = i0.ɵɵngDeclareInjectable({
34
34
  minVersion: "12.0.0",
35
- version: "22.2.0",
35
+ version: "22.2.2",
36
36
  ngImport: i0,
37
37
  type: RetryActionService,
38
38
  providedIn: "root"
@@ -40,7 +40,7 @@ var RetryActionService = class RetryActionService {
40
40
  };
41
41
  i0.ɵɵngDeclareClassMetadata({
42
42
  minVersion: "12.0.0",
43
- version: "22.2.0",
43
+ version: "22.2.2",
44
44
  ngImport: i0,
45
45
  type: RetryActionService,
46
46
  decorators: [{
@@ -90,13 +90,13 @@ var SparkLanguageService = class SparkLanguageService {
90
90
  if (!ts) return "";
91
91
  return ts[this.currentLang()] ?? ts["en"] ?? Object.values(ts)[0] ?? "";
92
92
  }
93
- t(key) {
93
+ t(key, fallback) {
94
94
  const ts = this.translationsMap()[key];
95
- return this.resolve(ts) || key;
95
+ return this.resolve(ts) || fallback || key;
96
96
  }
97
97
  static ɵfac = i0.ɵɵngDeclareFactory({
98
98
  minVersion: "12.0.0",
99
- version: "22.2.0",
99
+ version: "22.2.2",
100
100
  ngImport: i0,
101
101
  type: SparkLanguageService,
102
102
  deps: [],
@@ -104,7 +104,7 @@ var SparkLanguageService = class SparkLanguageService {
104
104
  });
105
105
  static ɵprov = i0.ɵɵngDeclareInjectable({
106
106
  minVersion: "12.0.0",
107
- version: "22.2.0",
107
+ version: "22.2.2",
108
108
  ngImport: i0,
109
109
  type: SparkLanguageService,
110
110
  providedIn: "root"
@@ -112,7 +112,7 @@ var SparkLanguageService = class SparkLanguageService {
112
112
  };
113
113
  i0.ɵɵngDeclareClassMetadata({
114
114
  minVersion: "12.0.0",
115
- version: "22.2.0",
115
+ version: "22.2.2",
116
116
  ngImport: i0,
117
117
  type: SparkLanguageService,
118
118
  decorators: [{
@@ -121,86 +121,392 @@ i0.ɵɵngDeclareClassMetadata({
121
121
  }],
122
122
  ctorParameters: () => []
123
123
  });
124
- var SparkStreamingService = class SparkStreamingService {
124
+ const BASE_DELAY_MS = 1e3;
125
+ const MAX_DELAY_MS = 3e4;
126
+ const REAUTH_LOOP_MS = 5e3;
127
+ const OPEN = 1;
128
+ var SparkLiveService = class SparkLiveService {
125
129
  config = inject(SPARK_CONFIG, { optional: true });
126
130
  baseUrl = this.config?.baseUrl ?? "/spark";
127
131
  ngZone = inject(NgZone);
128
- connectToStreamingQuery(queryId) {
132
+ entries = /* @__PURE__ */ new Map();
133
+ bySub = /* @__PURE__ */ new Map();
134
+ nextSub = 0;
135
+ socket = null;
136
+ reconnectTimer = null;
137
+ attempt = 0;
138
+ lastReauthAt = 0;
139
+ watchPersistentObject(objectTypeId, id, options) {
140
+ const request = {
141
+ objectTypeId,
142
+ id,
143
+ deleted: options?.deleted ?? null
144
+ };
145
+ return new Observable((subscriber) => {
146
+ const watcher = {
147
+ next: (event) => subscriber.next(event),
148
+ complete: () => subscriber.complete()
149
+ };
150
+ const entry = this.attach("po", request, watcher);
151
+ return () => this.detach(entry, watcher);
152
+ });
153
+ }
154
+ watchQuery(request) {
155
+ const requests = isObservable(request) ? request : of(request);
129
156
  return new Observable((subscriber) => {
130
- let ws = null;
131
- let retryCount = 0;
132
- let retryTimeout = null;
133
- let closed = false;
134
- const maxRetries = 10;
135
- const maxDelay = 3e4;
136
- const connect = () => {
137
- if (closed) return;
138
- const url = this.buildWebSocketUrl(queryId);
139
- ws = new WebSocket(url);
140
- ws.onopen = () => {
141
- retryCount = 0;
142
- };
143
- ws.onmessage = (event) => {
144
- try {
145
- const message = JSON.parse(event.data);
146
- this.ngZone.run(() => subscriber.next(message));
147
- } catch {}
148
- };
149
- ws.onerror = () => {};
150
- ws.onclose = (event) => {
151
- if (closed) return;
152
- if (event.code === 1e3) {
153
- this.ngZone.run(() => subscriber.complete());
154
- return;
155
- }
156
- if (retryCount < maxRetries) {
157
- const delay = Math.min(1e3 * Math.pow(2, retryCount), maxDelay);
158
- retryCount++;
159
- retryTimeout = setTimeout(() => connect(), delay);
160
- } else this.ngZone.run(() => subscriber.error(/* @__PURE__ */ new Error("WebSocket connection failed after maximum retries")));
161
- };
157
+ let entry = null;
158
+ let ended = false;
159
+ const watcher = {
160
+ next: (event) => subscriber.next(event),
161
+ complete: () => {
162
+ ended = true;
163
+ entry = null;
164
+ subscriber.complete();
165
+ }
162
166
  };
163
- connect();
167
+ const requestSubscription = requests.subscribe((next) => {
168
+ if (ended) return;
169
+ if (!next) {
170
+ if (entry) this.detach(entry, watcher);
171
+ entry = null;
172
+ return;
173
+ }
174
+ const key = keyOf("query", next);
175
+ if (entry?.key === key) return;
176
+ if (entry && entry.watchers.size === 1 && !this.entries.has(key)) {
177
+ this.update(entry, key, next);
178
+ return;
179
+ }
180
+ if (entry) this.detach(entry, watcher);
181
+ entry = this.attach("query", next, watcher);
182
+ });
164
183
  return () => {
165
- closed = true;
166
- if (retryTimeout) clearTimeout(retryTimeout);
167
- if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) ws.close(1e3, "Client unsubscribed");
184
+ requestSubscription.unsubscribe();
185
+ if (entry) this.detach(entry, watcher);
186
+ entry = null;
168
187
  };
169
188
  });
170
189
  }
171
- buildWebSocketUrl(queryId) {
172
- const encodedId = encodeURIComponent(queryId);
173
- const path = `${this.baseUrl}/queries/${encodedId}/stream`;
190
+ livePersistentObject(type, id, options) {
191
+ if (!options?.injector) assertInInjectionContext(this.livePersistentObject);
192
+ const injector = options?.injector ?? inject(Injector);
193
+ const value = signal(null, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
194
+ effect((onCleanup) => {
195
+ const objectTypeId = type();
196
+ const objectId = id();
197
+ untracked(() => value.set(null));
198
+ if (!objectTypeId || !objectId) return;
199
+ const subscription = this.watchPersistentObject(objectTypeId, objectId).subscribe((event) => {
200
+ if (event.type === "po") value.set(event.po);
201
+ else if (event.type === "gone") value.set("gone");
202
+ else value.set(null);
203
+ });
204
+ onCleanup(() => subscription.unsubscribe());
205
+ }, { injector });
206
+ return value.asReadonly();
207
+ }
208
+ liveQuery(request, options) {
209
+ if (!options?.injector) assertInInjectionContext(this.liveQuery);
210
+ const injector = options?.injector ?? inject(Injector);
211
+ const value = signal(null, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
212
+ const requests = new BehaviorSubject(null);
213
+ const subscription = this.watchQuery(requests).subscribe((event) => {
214
+ value.set(event.type === "result" ? event.result : null);
215
+ });
216
+ effect(() => {
217
+ const next = request();
218
+ untracked(() => requests.next(next));
219
+ }, { injector });
220
+ injector.get(DestroyRef).onDestroy(() => {
221
+ subscription.unsubscribe();
222
+ requests.complete();
223
+ });
224
+ return value.asReadonly();
225
+ }
226
+ attach(kind, request, watcher) {
227
+ const key = keyOf(kind, request);
228
+ let entry = this.entries.get(key);
229
+ if (entry) {
230
+ entry.watchers.add(watcher);
231
+ if (entry.state) watcher.next(kind === "po" ? {
232
+ type: "po",
233
+ po: entry.state
234
+ } : {
235
+ type: "result",
236
+ result: entry.state
237
+ });
238
+ return entry;
239
+ }
240
+ entry = {
241
+ key,
242
+ sub: `s${++this.nextSub}`,
243
+ kind,
244
+ request: clean(request),
245
+ watchers: /* @__PURE__ */ new Set([watcher]),
246
+ state: null
247
+ };
248
+ this.entries.set(key, entry);
249
+ this.bySub.set(entry.sub, entry);
250
+ if (this.isOpen()) this.sendSubscribe(entry);
251
+ else this.ensureSocket();
252
+ return entry;
253
+ }
254
+ update(entry, key, request) {
255
+ this.entries.delete(entry.key);
256
+ entry.key = key;
257
+ entry.request = clean(request);
258
+ this.entries.set(key, entry);
259
+ if (this.isOpen()) this.send({
260
+ op: "update",
261
+ sub: entry.sub,
262
+ query: entry.request
263
+ });
264
+ }
265
+ detach(entry, watcher) {
266
+ if (!entry.watchers.delete(watcher)) return;
267
+ if (entry.watchers.size > 0 || this.entries.get(entry.key) !== entry) return;
268
+ this.forget(entry);
269
+ if (this.isOpen()) this.send({
270
+ op: "unsubscribe",
271
+ sub: entry.sub
272
+ });
273
+ this.closeIfIdle();
274
+ }
275
+ forget(entry) {
276
+ if (this.entries.get(entry.key) === entry) this.entries.delete(entry.key);
277
+ this.bySub.delete(entry.sub);
278
+ }
279
+ end(entry, event) {
280
+ this.forget(entry);
281
+ const watchers = [...entry.watchers];
282
+ entry.watchers.clear();
283
+ this.emit(() => {
284
+ for (const watcher of watchers) {
285
+ watcher.next(event);
286
+ watcher.complete();
287
+ }
288
+ });
289
+ }
290
+ failAll(code, message) {
291
+ const event = {
292
+ type: "error",
293
+ code,
294
+ message
295
+ };
296
+ for (const entry of [...this.entries.values()]) this.end(entry, event);
297
+ this.closeSocket();
298
+ }
299
+ isOpen() {
300
+ return this.socket !== null && this.socket.readyState === OPEN;
301
+ }
302
+ ensureSocket() {
303
+ if (this.socket || this.reconnectTimer) return;
304
+ this.open();
305
+ }
306
+ open() {
307
+ this.ngZone.runOutsideAngular(() => {
308
+ const socket = new WebSocket(this.buildWebSocketUrl());
309
+ this.socket = socket;
310
+ socket.onopen = () => {
311
+ if (this.socket !== socket) return;
312
+ for (const entry of this.entries.values()) this.sendSubscribe(entry);
313
+ };
314
+ socket.onmessage = (event) => {
315
+ if (this.socket !== socket) return;
316
+ this.onFrame(event.data);
317
+ };
318
+ socket.onerror = () => {};
319
+ socket.onclose = (event) => {
320
+ if (this.socket !== socket) return;
321
+ this.socket = null;
322
+ this.onClose(event.code);
323
+ };
324
+ });
325
+ }
326
+ onClose(code) {
327
+ if (this.entries.size === 0) return;
328
+ for (const entry of this.entries.values()) entry.state = null;
329
+ if (code === LIVE_CLOSE_TOO_MANY_SOCKETS) {
330
+ this.failAll("tooManySockets", "Too many live connections are open for this user.");
331
+ return;
332
+ }
333
+ if (code === LIVE_CLOSE_MESSAGE_TOO_LARGE) {
334
+ this.failAll("messageTooLarge", "A live request was too large.");
335
+ return;
336
+ }
337
+ if (code === LIVE_CLOSE_REAUTHENTICATE) {
338
+ const now = Date.now();
339
+ if (this.lastReauthAt && now - this.lastReauthAt < REAUTH_LOOP_MS) {
340
+ this.lastReauthAt = 0;
341
+ this.failAll("unauthenticated", "The live connection was refused: sign in again.");
342
+ return;
343
+ }
344
+ this.lastReauthAt = now;
345
+ this.open();
346
+ return;
347
+ }
348
+ const delay = Math.min(BASE_DELAY_MS * Math.pow(2, this.attempt), MAX_DELAY_MS) * (.5 + Math.random() / 2);
349
+ this.attempt++;
350
+ this.ngZone.runOutsideAngular(() => {
351
+ this.reconnectTimer = setTimeout(() => {
352
+ this.reconnectTimer = null;
353
+ if (this.entries.size > 0) this.open();
354
+ }, delay);
355
+ });
356
+ }
357
+ closeIfIdle() {
358
+ if (this.entries.size > 0) return;
359
+ Promise.resolve().then(() => {
360
+ if (this.entries.size === 0) this.closeSocket();
361
+ });
362
+ }
363
+ closeSocket() {
364
+ if (this.reconnectTimer) {
365
+ clearTimeout(this.reconnectTimer);
366
+ this.reconnectTimer = null;
367
+ }
368
+ const socket = this.socket;
369
+ this.socket = null;
370
+ this.attempt = 0;
371
+ if (socket && socket.readyState <= OPEN) socket.close(1e3, "No live subscriptions");
372
+ }
373
+ sendSubscribe(entry) {
374
+ this.send(entry.kind === "po" ? {
375
+ op: "subscribe",
376
+ sub: entry.sub,
377
+ po: entry.request
378
+ } : {
379
+ op: "subscribe",
380
+ sub: entry.sub,
381
+ query: entry.request
382
+ });
383
+ }
384
+ send(op) {
385
+ this.socket?.send(JSON.stringify(op));
386
+ }
387
+ onFrame(data) {
388
+ let frame;
389
+ try {
390
+ frame = JSON.parse(String(data));
391
+ } catch {
392
+ return;
393
+ }
394
+ if (!frame || typeof frame !== "object") return;
395
+ this.attempt = 0;
396
+ if (frame.type === "ping") return;
397
+ if (frame.type === "error" && !frame.sub) {
398
+ console.warn(`[spark] live: ${frame.code}: ${frame.message}`);
399
+ return;
400
+ }
401
+ const entry = this.bySub.get(frame.sub);
402
+ if (!entry) return;
403
+ switch (frame.type) {
404
+ case "po":
405
+ entry.state = frame.po;
406
+ this.broadcast(entry, {
407
+ type: "po",
408
+ po: frame.po
409
+ });
410
+ break;
411
+ case "poPatch":
412
+ if (!entry.state) return;
413
+ entry.state = applyLivePoPatch(entry.state, frame.patch);
414
+ this.broadcast(entry, {
415
+ type: "po",
416
+ po: entry.state
417
+ });
418
+ break;
419
+ case "gone":
420
+ this.end(entry, { type: "gone" });
421
+ this.closeIfIdle();
422
+ break;
423
+ case "result":
424
+ entry.state = frame.result;
425
+ this.broadcast(entry, {
426
+ type: "result",
427
+ result: frame.result
428
+ });
429
+ break;
430
+ case "patch":
431
+ if (!entry.state) return;
432
+ entry.state = applyLiveQueryPatch(entry.state, frame.patch);
433
+ this.broadcast(entry, {
434
+ type: "result",
435
+ result: entry.state
436
+ });
437
+ break;
438
+ case "error":
439
+ this.end(entry, {
440
+ type: "error",
441
+ code: frame.code,
442
+ message: frame.message
443
+ });
444
+ this.closeIfIdle();
445
+ }
446
+ }
447
+ broadcast(entry, event) {
448
+ const watchers = [...entry.watchers];
449
+ this.emit(() => {
450
+ for (const watcher of watchers) watcher.next(event);
451
+ });
452
+ }
453
+ emit(fn) {
454
+ this.ngZone.run(fn);
455
+ }
456
+ buildWebSocketUrl() {
457
+ const path = `${this.baseUrl}/live`;
174
458
  if (this.baseUrl.startsWith("http://")) return path.replace(/^http:\/\//, "ws://");
175
459
  if (this.baseUrl.startsWith("https://")) return path.replace(/^https:\/\//, "wss://");
176
460
  return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}${path}`;
177
461
  }
178
462
  static ɵfac = i0.ɵɵngDeclareFactory({
179
463
  minVersion: "12.0.0",
180
- version: "22.2.0",
464
+ version: "22.2.2",
181
465
  ngImport: i0,
182
- type: SparkStreamingService,
466
+ type: SparkLiveService,
183
467
  deps: [],
184
468
  target: i0.ɵɵFactoryTarget.Injectable
185
469
  });
186
470
  static ɵprov = i0.ɵɵngDeclareInjectable({
187
471
  minVersion: "12.0.0",
188
- version: "22.2.0",
472
+ version: "22.2.2",
189
473
  ngImport: i0,
190
- type: SparkStreamingService,
474
+ type: SparkLiveService,
191
475
  providedIn: "root"
192
476
  });
193
477
  };
194
478
  i0.ɵɵngDeclareClassMetadata({
195
479
  minVersion: "12.0.0",
196
- version: "22.2.0",
480
+ version: "22.2.2",
197
481
  ngImport: i0,
198
- type: SparkStreamingService,
482
+ type: SparkLiveService,
199
483
  decorators: [{
200
484
  type: Injectable,
201
485
  args: [{ providedIn: "root" }]
202
486
  }]
203
487
  });
488
+ function clean(request) {
489
+ return JSON.parse(canonicalJson(request));
490
+ }
491
+ function keyOf(kind, request) {
492
+ return `${kind}:${canonicalJson(request)}`;
493
+ }
494
+ function canonicalJson(value) {
495
+ return JSON.stringify(normalize(value));
496
+ }
497
+ function normalize(value) {
498
+ if (Array.isArray(value)) return value.map(normalize);
499
+ if (value && typeof value === "object") {
500
+ const result = {};
501
+ for (const key of Object.keys(value).sort()) {
502
+ const field = value[key];
503
+ if (field === null || field === void 0) continue;
504
+ result[key] = normalize(field);
505
+ }
506
+ return result;
507
+ }
508
+ return value;
509
+ }
204
510
  var SparkService = class SparkService {
205
511
  config = inject(SPARK_CONFIG, { optional: true });
206
512
  baseUrl = this.config?.baseUrl ?? "/spark";
@@ -423,7 +729,7 @@ var SparkService = class SparkService {
423
729
  }
424
730
  static ɵfac = i0.ɵɵngDeclareFactory({
425
731
  minVersion: "12.0.0",
426
- version: "22.2.0",
732
+ version: "22.2.2",
427
733
  ngImport: i0,
428
734
  type: SparkService,
429
735
  deps: [],
@@ -431,7 +737,7 @@ var SparkService = class SparkService {
431
737
  });
432
738
  static ɵprov = i0.ɵɵngDeclareInjectable({
433
739
  minVersion: "12.0.0",
434
- version: "22.2.0",
740
+ version: "22.2.2",
435
741
  ngImport: i0,
436
742
  type: SparkService,
437
743
  providedIn: "root"
@@ -439,7 +745,7 @@ var SparkService = class SparkService {
439
745
  };
440
746
  i0.ɵɵngDeclareClassMetadata({
441
747
  minVersion: "12.0.0",
442
- version: "22.2.0",
748
+ version: "22.2.2",
443
749
  ngImport: i0,
444
750
  type: SparkService,
445
751
  decorators: [{
@@ -449,6 +755,8 @@ i0.ɵɵngDeclareClassMetadata({
449
755
  });
450
756
  const SPARK_BUILT_IN_ICONS = {
451
757
  "three-dots-vertical": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-three-dots-vertical\" viewBox=\"0 0 16 16\"><path d=\"M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0\"/></svg>",
758
+ "bell": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-bell\" viewBox=\"0 0 16 16\"><path d=\"M8 16a2 2 0 0 0 2-2H6a2 2 0 0 0 2 2M8 1.918l-.797.161A4 4 0 0 0 4 6c0 .628-.134 2.197-.459 3.742-.16.767-.376 1.566-.663 2.258h10.244c-.287-.692-.502-1.49-.663-2.258C12.134 8.197 12 6.628 12 6a4 4 0 0 0-3.203-3.92zM14.22 12c.223.447.481.801.78 1H1c.299-.199.557-.553.78-1C2.68 10.2 3 6.88 3 6c0-2.42 1.72-4.44 4.005-4.901a1 1 0 1 1 1.99 0A5 5 0 0 1 13 6c0 .88.32 4.2 1.22 6\"/></svg>",
759
+ "share": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-share\" viewBox=\"0 0 16 16\"><path d=\"M13.5 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M11 2.5a2.5 2.5 0 1 1 .603 1.628l-6.718 3.12a2.5 2.5 0 0 1 0 1.504l6.718 3.12a2.5 2.5 0 1 1-.488.876l-6.718-3.12a2.5 2.5 0 1 1 0-3.256l6.718-3.12A2.5 2.5 0 0 1 11 2.5m-8.5 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m11 5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3\"/></svg>",
452
760
  "x-circle": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"/><path d=\"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708\"/></svg>",
453
761
  "arrow-left": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-left\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8\"/></svg>",
454
762
  "pencil": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-pencil\" viewBox=\"0 0 16 16\"><path d=\"M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325\"/></svg>",
@@ -456,7 +764,9 @@ const SPARK_BUILT_IN_ICONS = {
456
764
  "plus-lg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-plus-lg\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2\"/></svg>",
457
765
  "search": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-search\" viewBox=\"0 0 16 16\"><path d=\"M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0\"/></svg>",
458
766
  "trash": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-trash\" viewBox=\"0 0 16 16\"><path d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z\"/><path d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z\"/></svg>",
459
- "x-lg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-lg\" viewBox=\"0 0 16 16\"><path d=\"M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z\"/></svg>"
767
+ "x-lg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-lg\" viewBox=\"0 0 16 16\"><path d=\"M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z\"/></svg>",
768
+ "person": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-person\" viewBox=\"0 0 16 16\"><path d=\"M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z\"/></svg>",
769
+ "box-arrow-right": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-box-arrow-right\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0z\"/><path fill-rule=\"evenodd\" d=\"M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708z\"/></svg>"
460
770
  };
461
771
  var SparkIconRegistry = class SparkIconRegistry {
462
772
  sanitizer = inject(DomSanitizer);
@@ -475,7 +785,7 @@ var SparkIconRegistry = class SparkIconRegistry {
475
785
  }
476
786
  static ɵfac = i0.ɵɵngDeclareFactory({
477
787
  minVersion: "12.0.0",
478
- version: "22.2.0",
788
+ version: "22.2.2",
479
789
  ngImport: i0,
480
790
  type: SparkIconRegistry,
481
791
  deps: [],
@@ -483,7 +793,7 @@ var SparkIconRegistry = class SparkIconRegistry {
483
793
  });
484
794
  static ɵprov = i0.ɵɵngDeclareInjectable({
485
795
  minVersion: "12.0.0",
486
- version: "22.2.0",
796
+ version: "22.2.2",
487
797
  ngImport: i0,
488
798
  type: SparkIconRegistry,
489
799
  providedIn: "root"
@@ -491,7 +801,7 @@ var SparkIconRegistry = class SparkIconRegistry {
491
801
  };
492
802
  i0.ɵɵngDeclareClassMetadata({
493
803
  minVersion: "12.0.0",
494
- version: "22.2.0",
804
+ version: "22.2.2",
495
805
  ngImport: i0,
496
806
  type: SparkIconRegistry,
497
807
  decorators: [{
@@ -523,7 +833,7 @@ var SparkQueryActionsService = class SparkQueryActionsService {
523
833
  }
524
834
  static ɵfac = i0.ɵɵngDeclareFactory({
525
835
  minVersion: "12.0.0",
526
- version: "22.2.0",
836
+ version: "22.2.2",
527
837
  ngImport: i0,
528
838
  type: SparkQueryActionsService,
529
839
  deps: [],
@@ -531,7 +841,7 @@ var SparkQueryActionsService = class SparkQueryActionsService {
531
841
  });
532
842
  static ɵprov = i0.ɵɵngDeclareInjectable({
533
843
  minVersion: "12.0.0",
534
- version: "22.2.0",
844
+ version: "22.2.2",
535
845
  ngImport: i0,
536
846
  type: SparkQueryActionsService,
537
847
  providedIn: "root"
@@ -539,7 +849,7 @@ var SparkQueryActionsService = class SparkQueryActionsService {
539
849
  };
540
850
  i0.ɵɵngDeclareClassMetadata({
541
851
  minVersion: "12.0.0",
542
- version: "22.2.0",
852
+ version: "22.2.2",
543
853
  ngImport: i0,
544
854
  type: SparkQueryActionsService,
545
855
  decorators: [{
@@ -632,7 +942,7 @@ var SparkReturnNavigationService = class SparkReturnNavigationService {
632
942
  }
633
943
  static ɵfac = i0.ɵɵngDeclareFactory({
634
944
  minVersion: "12.0.0",
635
- version: "22.2.0",
945
+ version: "22.2.2",
636
946
  ngImport: i0,
637
947
  type: SparkReturnNavigationService,
638
948
  deps: [],
@@ -640,7 +950,7 @@ var SparkReturnNavigationService = class SparkReturnNavigationService {
640
950
  });
641
951
  static ɵprov = i0.ɵɵngDeclareInjectable({
642
952
  minVersion: "12.0.0",
643
- version: "22.2.0",
953
+ version: "22.2.2",
644
954
  ngImport: i0,
645
955
  type: SparkReturnNavigationService,
646
956
  providedIn: "root"
@@ -648,7 +958,7 @@ var SparkReturnNavigationService = class SparkReturnNavigationService {
648
958
  };
649
959
  i0.ɵɵngDeclareClassMetadata({
650
960
  minVersion: "12.0.0",
651
- version: "22.2.0",
961
+ version: "22.2.2",
652
962
  ngImport: i0,
653
963
  type: SparkReturnNavigationService,
654
964
  decorators: [{
@@ -673,6 +983,48 @@ function sparkDatatableLabels(lang) {
673
983
  resetColumn: lang.t("common.resetColumn")
674
984
  };
675
985
  }
676
- export { RetryActionService, SPARK_RETURN_URL_STATE_KEY, SPARK_TIMEZONE_COOKIE, SPARK_TIMEZONE_HEADER, SparkIconRegistry, SparkLanguageService, SparkQueryActionsService, SparkReturnNavigationService, SparkService, SparkStreamingService, createSparkTimezoneInterceptor, resolveBrowserTimeZone, sparkDatatableLabels, sparkTimezoneInterceptor, withSparkTimezone };
986
+ function sparkRetryAfterSeconds(e) {
987
+ const err = e;
988
+ if (err?.status !== 429) return null;
989
+ const body = err.error;
990
+ const fromBody = typeof body === "object" && body ? body.result?.retryAfterSeconds ?? body.retryAfterSeconds : void 0;
991
+ if (typeof fromBody === "number" && Number.isFinite(fromBody) && fromBody >= 0) return Math.ceil(fromBody);
992
+ const header = err.headers?.get?.("Retry-After");
993
+ if (header && /^\d+$/.test(header.trim())) return Number(header.trim());
994
+ return null;
995
+ }
996
+ function formatSparkWait(seconds, lang) {
997
+ const s = Math.max(0, Math.ceil(seconds));
998
+ const say = (unit, n) => lang.t(`spark.wait.${unit}${n === 1 ? "" : "s"}`, n === 1 ? `1 ${unit}` : `{0} ${unit}s`).replace("{0}", String(n));
999
+ if (s < 60) return lang.t("spark.wait.fewSeconds", "a few seconds");
1000
+ if (s < 3600) return say("minute", Math.ceil(s / 60));
1001
+ if (s < 172800) return say("hour", Math.max(1, Math.round(s / 3600)));
1002
+ return say("day", Math.round(s / 86400));
1003
+ }
1004
+ function describeSparkError(e, lang, fallback) {
1005
+ const body = e?.error;
1006
+ let message = null;
1007
+ if (typeof body === "object" && body) {
1008
+ const first = body.result?.errors?.[0]?.errorMessage;
1009
+ if (typeof first === "string" && first) message = first;
1010
+ else if (first && typeof first === "object") message = lang.resolve(first) || null;
1011
+ if (!message && typeof body.result?.error === "string" && body.result.error) message = body.result.error;
1012
+ if (!message && typeof body.error === "string" && body.error) message = body.error;
1013
+ }
1014
+ message ??= fallback;
1015
+ const wait = sparkRetryAfterSeconds(e);
1016
+ if (wait === null) return message;
1017
+ const tryAgain = lang.t("spark.tryAgainIn", "Try again in {0}.").replace("{0}", formatSparkWait(wait, lang));
1018
+ return `${message.trim()} ${tryAgain}`;
1019
+ }
1020
+ const sparkLanguageInterceptor = (req, next) => {
1021
+ const language = currentLanguage();
1022
+ if (!language) return next(req);
1023
+ return next(req.clone({ setHeaders: { "Accept-Language": language } }));
1024
+ };
1025
+ function withSparkLanguage() {
1026
+ return [withInterceptors([sparkLanguageInterceptor])];
1027
+ }
1028
+ export { RetryActionService, SPARK_RETURN_URL_STATE_KEY, SPARK_TIMEZONE_COOKIE, SPARK_TIMEZONE_HEADER, SparkIconRegistry, SparkLanguageService, SparkLiveService, SparkQueryActionsService, SparkReturnNavigationService, SparkService, canonicalJson, createSparkTimezoneInterceptor, describeSparkError, formatSparkWait, resolveBrowserTimeZone, sparkDatatableLabels, sparkLanguageInterceptor, sparkRetryAfterSeconds, sparkTimezoneInterceptor, withSparkLanguage, withSparkTimezone };
677
1029
 
678
1030
  //# sourceMappingURL=mintplayer-ng-spark-services.mjs.map