@acorex-platform/module-activity-insight-client 0.0.20

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.
@@ -0,0 +1,1606 @@
1
+ import { AXPHookService, AXPBroadcastEventService, AXP_MODULE_MANIFEST_PROVIDER, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY } from '@acorex-platform/framework-client/core';
2
+ import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/auth';
3
+ import { AXPEntityService, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_MODIFIER } from '@acorex-platform/framework-client/layout/entity';
4
+ import { AXPRuntimeTransportService, AXPCommandEventsService, AXPRuntimeModule } from '@acorex-platform/framework-client/runtime';
5
+ import * as i0 from '@angular/core';
6
+ import { inject, Injectable, DestroyRef, NgModule, InjectionToken } from '@angular/core';
7
+ import { Router, NavigationEnd, ROUTES } from '@angular/router';
8
+ import * as i1 from '@acorex-platform/framework-client/common';
9
+ import { AXPSearchCommandProvider, AXPSettingsService, AXPCommonModule, AXP_MENU_PROVIDER, AXP_SETTING_DEFINITION_PROVIDER, AXP_SEARCH_PROVIDER } from '@acorex-platform/framework-client/common';
10
+ import { RootConfig, AXMActivityInsightMenuKeys, AXPActivityInsightSettings, AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG, AXPInsightsReportingPermissionKeys, maskActivityProperties, truncateActivityProperties, AXMActivityEventNames } from '@acorex-platform/module-activity-insight-client/contracts';
11
+ import { AXPWidgetsList } from '@acorex-platform/framework-shared/core';
12
+ import { AXPCommonPermissionGroupKeys } from '@acorex-platform/module-common-client/contracts';
13
+ import { distinctUntilChanged, filter } from 'rxjs';
14
+ import { AXMActivityInsightTransportKeys } from '@acorex-platform/module-activity-insight-contracts';
15
+
16
+ //#region ---- Imports ----
17
+ //#endregion
18
+ //#region ---- Manifest Definition ----
19
+ const ActivityInsightManifest = {
20
+ name: RootConfig.module.name,
21
+ version: '1.0.0',
22
+ title: RootConfig.module.title,
23
+ icon: RootConfig.module.icon,
24
+ i18n: RootConfig.config.i18n,
25
+ type: 'platform',
26
+ domain: 'platform',
27
+ dependencies: [],
28
+ };
29
+ //#endregion
30
+
31
+ //#region ---- Imports ----
32
+ const ADMINISTRATION_MENU_KEY = 'Common:Menu:Administration';
33
+ //#endregion
34
+ class AXMActivityInsightMenuProvider {
35
+ constructor() {
36
+ this.entityService = inject(AXPEntityService);
37
+ this.sessionService = inject(AXPSessionService);
38
+ }
39
+ featurePath(segment) {
40
+ const app = this.sessionService.application?.name ?? 'app';
41
+ return `/${app}/m/${RootConfig.module.name}/${segment}`;
42
+ }
43
+ async provide(context) {
44
+ const module = RootConfig.module;
45
+ const entityPath = (entityName) => this.entityService.createPath(module.name, entityName);
46
+ context.find(ADMINISTRATION_MENU_KEY).addItems([
47
+ {
48
+ name: AXMActivityInsightMenuKeys.ActivityInsight.Root,
49
+ priority: 4,
50
+ text: module.title,
51
+ icon: module.icon,
52
+ type: 'menu',
53
+ policy: { features: [module.name] },
54
+ children: [
55
+ {
56
+ name: AXMActivityInsightMenuKeys.ActivityInsight.Events,
57
+ text: RootConfig.entities.activityEvent.titlePlural,
58
+ path: entityPath(RootConfig.entities.activityEvent.name),
59
+ icon: RootConfig.entities.activityEvent.icon,
60
+ priority: 1,
61
+ },
62
+ {
63
+ name: AXMActivityInsightMenuKeys.ActivityInsight.Timelines,
64
+ text: '@activity-insight:module.menus.timelines.title',
65
+ path: this.featurePath('timelines'),
66
+ icon: 'fa-light fa-timeline',
67
+ priority: 2,
68
+ },
69
+ {
70
+ name: AXMActivityInsightMenuKeys.ActivityInsight.Policies,
71
+ text: '@activity-insight:module.menus.policies.title',
72
+ type: 'menu',
73
+ icon: 'fa-light fa-sliders',
74
+ priority: 3,
75
+ children: [
76
+ {
77
+ name: 'ActivityInsight:Menu:TrackingRules',
78
+ text: RootConfig.entities.trackingRule.titlePlural,
79
+ path: entityPath(RootConfig.entities.trackingRule.name),
80
+ icon: RootConfig.entities.trackingRule.icon,
81
+ priority: 1,
82
+ },
83
+ {
84
+ name: 'ActivityInsight:Menu:RetentionPolicies',
85
+ text: RootConfig.entities.retentionPolicy.titlePlural,
86
+ path: entityPath(RootConfig.entities.retentionPolicy.name),
87
+ icon: RootConfig.entities.retentionPolicy.icon,
88
+ priority: 2,
89
+ },
90
+ {
91
+ name: 'ActivityInsight:Menu:MaskingPolicies',
92
+ text: RootConfig.entities.maskingPolicy.titlePlural,
93
+ path: entityPath(RootConfig.entities.maskingPolicy.name),
94
+ icon: RootConfig.entities.maskingPolicy.icon,
95
+ priority: 3,
96
+ },
97
+ {
98
+ name: 'ActivityInsight:Menu:SamplingPolicies',
99
+ text: RootConfig.entities.samplingPolicy.titlePlural,
100
+ path: entityPath(RootConfig.entities.samplingPolicy.name),
101
+ icon: RootConfig.entities.samplingPolicy.icon,
102
+ priority: 4,
103
+ },
104
+ {
105
+ name: 'ActivityInsight:Menu:DataSources',
106
+ text: RootConfig.entities.activityDataSource.titlePlural,
107
+ path: entityPath(RootConfig.entities.activityDataSource.name),
108
+ icon: RootConfig.entities.activityDataSource.icon,
109
+ priority: 5,
110
+ },
111
+ {
112
+ name: 'ActivityInsight:Menu:SavedViews',
113
+ text: RootConfig.entities.activitySavedView.titlePlural,
114
+ path: entityPath(RootConfig.entities.activitySavedView.name),
115
+ icon: RootConfig.entities.activitySavedView.icon,
116
+ priority: 6,
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ },
122
+ ]);
123
+ }
124
+ }
125
+
126
+ class AXPActivityInsightSettingProvider {
127
+ async provide(context) {
128
+ const trackingVisibility = `{{ context.eval('${AXPActivityInsightSettings.Enabled}') }}`;
129
+ const defaults = AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG;
130
+ context
131
+ .addGroup('activity-insight', '@activity-insight:settings.group.title', '@activity-insight:settings.group.description', 'fa-light fa-chart-network', undefined, 910)
132
+ .addSection('tracking', '@activity-insight:settings.tracking.title', '@activity-insight:settings.tracking.description')
133
+ .addSetting({
134
+ key: AXPActivityInsightSettings.Enabled,
135
+ title: '@activity-insight:settings.tracking.items.enabled.title',
136
+ description: '@activity-insight:settings.tracking.items.enabled.description',
137
+ scope: 'P',
138
+ overridableAt: [],
139
+ isInherited: false,
140
+ defaultValue: true,
141
+ widget: { type: AXPWidgetsList.Editors.ToggleSwitch },
142
+ })
143
+ .endSection()
144
+ .addSection('buffer', '@activity-insight:settings.buffer.title', '@activity-insight:settings.buffer.description')
145
+ .addSetting({
146
+ key: AXPActivityInsightSettings.FlushIntervalMs,
147
+ title: '@activity-insight:settings.buffer.items.flush-interval.title',
148
+ description: '@activity-insight:settings.buffer.items.flush-interval.description',
149
+ scope: 'P',
150
+ overridableAt: [],
151
+ isInherited: false,
152
+ defaultValue: defaults.flushIntervalMs,
153
+ widget: {
154
+ type: AXPWidgetsList.Editors.NumberBox,
155
+ options: { minValue: 1000, maxValue: 60000, step: 1000, suffix: 'ms', visibility: trackingVisibility },
156
+ },
157
+ })
158
+ .addSetting({
159
+ key: AXPActivityInsightSettings.MaxBatchSize,
160
+ title: '@activity-insight:settings.buffer.items.max-batch-size.title',
161
+ description: '@activity-insight:settings.buffer.items.max-batch-size.description',
162
+ scope: 'P',
163
+ overridableAt: [],
164
+ isInherited: false,
165
+ defaultValue: defaults.maxBatchSize,
166
+ widget: {
167
+ type: AXPWidgetsList.Editors.NumberBox,
168
+ options: { minValue: 1, maxValue: 500, visibility: trackingVisibility },
169
+ },
170
+ })
171
+ .addSetting({
172
+ key: AXPActivityInsightSettings.MaxPayloadBytes,
173
+ title: '@activity-insight:settings.buffer.items.max-payload-bytes.title',
174
+ description: '@activity-insight:settings.buffer.items.max-payload-bytes.description',
175
+ scope: 'P',
176
+ overridableAt: [],
177
+ isInherited: false,
178
+ defaultValue: defaults.maxPayloadBytes,
179
+ widget: {
180
+ type: AXPWidgetsList.Editors.NumberBox,
181
+ options: { minValue: 4096, maxValue: 65536, visibility: trackingVisibility },
182
+ },
183
+ })
184
+ .addSetting({
185
+ key: AXPActivityInsightSettings.MaxQueueSize,
186
+ title: '@activity-insight:settings.buffer.items.max-queue-size.title',
187
+ description: '@activity-insight:settings.buffer.items.max-queue-size.description',
188
+ scope: 'P',
189
+ overridableAt: [],
190
+ isInherited: false,
191
+ defaultValue: defaults.maxQueueSize,
192
+ widget: {
193
+ type: AXPWidgetsList.Editors.NumberBox,
194
+ options: { minValue: 100, maxValue: 10000, visibility: trackingVisibility },
195
+ },
196
+ })
197
+ .addSetting({
198
+ key: AXPActivityInsightSettings.FlushOnVisibilityHidden,
199
+ title: '@activity-insight:settings.buffer.items.flush-on-hidden.title',
200
+ description: '@activity-insight:settings.buffer.items.flush-on-hidden.description',
201
+ scope: 'P',
202
+ overridableAt: [],
203
+ isInherited: false,
204
+ defaultValue: defaults.flushOnVisibilityHidden,
205
+ widget: {
206
+ type: AXPWidgetsList.Editors.ToggleSwitch,
207
+ options: { visibility: trackingVisibility },
208
+ },
209
+ })
210
+ .addSetting({
211
+ key: AXPActivityInsightSettings.UseBeaconForFinalFlush,
212
+ title: '@activity-insight:settings.buffer.items.use-beacon.title',
213
+ description: '@activity-insight:settings.buffer.items.use-beacon.description',
214
+ scope: 'P',
215
+ overridableAt: [],
216
+ isInherited: false,
217
+ defaultValue: defaults.useBeaconForFinalFlush,
218
+ widget: {
219
+ type: AXPWidgetsList.Editors.ToggleSwitch,
220
+ options: { visibility: trackingVisibility },
221
+ },
222
+ })
223
+ .endSection()
224
+ .addSection('privacy', '@activity-insight:settings.privacy.title', '@activity-insight:settings.privacy.description')
225
+ .addSetting({
226
+ key: AXPActivityInsightSettings.MaskingEnabled,
227
+ title: '@activity-insight:settings.privacy.items.masking-enabled.title',
228
+ description: '@activity-insight:settings.privacy.items.masking-enabled.description',
229
+ scope: 'P',
230
+ overridableAt: [],
231
+ isInherited: false,
232
+ defaultValue: true,
233
+ widget: {
234
+ type: AXPWidgetsList.Editors.ToggleSwitch,
235
+ options: { visibility: trackingVisibility },
236
+ },
237
+ })
238
+ .addSetting({
239
+ key: AXPActivityInsightSettings.SamplingEnabled,
240
+ title: '@activity-insight:settings.privacy.items.sampling-enabled.title',
241
+ description: '@activity-insight:settings.privacy.items.sampling-enabled.description',
242
+ scope: 'P',
243
+ overridableAt: [],
244
+ isInherited: false,
245
+ defaultValue: true,
246
+ widget: {
247
+ type: AXPWidgetsList.Editors.ToggleSwitch,
248
+ options: { visibility: trackingVisibility },
249
+ },
250
+ })
251
+ .addSetting({
252
+ key: AXPActivityInsightSettings.TrackAiContent,
253
+ title: '@activity-insight:settings.privacy.items.track-ai-content.title',
254
+ description: '@activity-insight:settings.privacy.items.track-ai-content.description',
255
+ scope: 'P',
256
+ overridableAt: [],
257
+ isInherited: false,
258
+ defaultValue: false,
259
+ widget: {
260
+ type: AXPWidgetsList.Editors.ToggleSwitch,
261
+ options: { visibility: trackingVisibility },
262
+ },
263
+ })
264
+ .endSection()
265
+ .endGroup();
266
+ }
267
+ }
268
+
269
+ class AXMActivityInsightSearchCommandProvider extends AXPSearchCommandProvider {
270
+ constructor() {
271
+ super(...arguments);
272
+ this.commands = [];
273
+ }
274
+ }
275
+
276
+ //#endregion
277
+ //#region ----- Permission Definition Provider -----
278
+ /**
279
+ * Registers activity-insight capabilities into the shared Insights & Reporting group.
280
+ */
281
+ class AXMActivityInsightPermissionDefinitionProvider {
282
+ async define(context) {
283
+ const keys = AXPInsightsReportingPermissionKeys;
284
+ const groups = AXPCommonPermissionGroupKeys;
285
+ const P = '@common:insights-reporting.permissions';
286
+ const G = '@common:permission-groups';
287
+ context.addGroup(groups.InsightsReporting.Root, `${G}.insights-reporting.title`, `${G}.insights-reporting.description`, { order: 100 });
288
+ context
289
+ .addGroup(groups.InsightsReporting.ActivityInsight, `${G}.insights-reporting-activity-insight.title`, `${G}.insights-reporting-activity-insight.description`, { parent: groups.InsightsReporting.Root, order: 20 })
290
+ .addPermission(keys.ViewActivityInsight, `${P}.view-activity-insight.title`, `${P}.view-activity-insight.description`)
291
+ .endPermission()
292
+ .addPermission(keys.ManageActivityInsight, `${P}.manage-activity-insight.title`, `${P}.manage-activity-insight.description`)
293
+ .endPermission()
294
+ .endGroup();
295
+ }
296
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPermissionDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
297
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPermissionDefinitionProvider }); }
298
+ }
299
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPermissionDefinitionProvider, decorators: [{
300
+ type: Injectable
301
+ }] });
302
+
303
+ const activityInsightFeatureRoutes = [
304
+ {
305
+ path: `:app/m/${RootConfig.module.name}`,
306
+ children: [
307
+ {
308
+ path: '',
309
+ redirectTo: `${RootConfig.entities.activityEvent.name}/list`,
310
+ pathMatch: 'full',
311
+ },
312
+ {
313
+ path: 'timelines',
314
+ loadComponent: () => import('./acorex-platform-module-activity-insight-client-activity-timeline-page.component-DULl2SRF.mjs').then((m) => m.AXMActivityInsightTimelinePageComponent),
315
+ },
316
+ {
317
+ path: 'dashboard',
318
+ loadComponent: () => import('./acorex-platform-module-activity-insight-client-activity-dashboard-page.component-CAcsbq4I.mjs').then((m) => m.AXMActivityInsightDashboardPageComponent),
319
+ },
320
+ ],
321
+ },
322
+ ];
323
+
324
+ const ACTIVITY_TRACKING_PLUGIN_NAME = 'tracking';
325
+ function normalizeActivityTrackingPluginOptions(options) {
326
+ const opts = options ?? {};
327
+ return {
328
+ enabled: opts.enabled ?? true,
329
+ list: {
330
+ open: opts.list?.open ?? true,
331
+ filterApply: opts.list?.filterApply ?? true,
332
+ search: opts.list?.search ?? true,
333
+ sort: opts.list?.sort ?? false,
334
+ export: opts.list?.export ?? false,
335
+ },
336
+ detail: {
337
+ open: opts.detail?.open ?? true,
338
+ tabChange: opts.detail?.tabChange ?? false,
339
+ },
340
+ form: {
341
+ createStart: opts.form?.createStart ?? false,
342
+ updateStart: opts.form?.updateStart ?? false,
343
+ submit: opts.form?.submit ?? true,
344
+ validationError: opts.form?.validationError ?? true,
345
+ fieldChange: {
346
+ enabled: opts.form?.fieldChange?.enabled ?? false,
347
+ include: opts.form?.fieldChange?.include ?? [],
348
+ exclude: opts.form?.fieldChange?.exclude ?? [],
349
+ },
350
+ },
351
+ commands: opts.commands ?? { '*': true },
352
+ queries: opts.queries ?? { list: true, byKey: false, lookup: false },
353
+ masking: opts.masking ?? { fields: [] },
354
+ sampling: opts.sampling ?? {},
355
+ };
356
+ }
357
+ function isActivityTrackingEnabled(options) {
358
+ return normalizeActivityTrackingPluginOptions(options).enabled !== false;
359
+ }
360
+ const activityTrackingPlugin = {
361
+ name: ACTIVITY_TRACKING_PLUGIN_NAME,
362
+ order: 1,
363
+ apply: (ctx, options) => {
364
+ const normalized = normalizeActivityTrackingPluginOptions(options);
365
+ const existing = ctx.plugins.find(ACTIVITY_TRACKING_PLUGIN_NAME).get();
366
+ if (existing) {
367
+ ctx.plugins.find(ACTIVITY_TRACKING_PLUGIN_NAME).update((p) => ({ ...p, options: normalized }));
368
+ }
369
+ else {
370
+ ctx.plugins.add({ name: ACTIVITY_TRACKING_PLUGIN_NAME, options: normalized });
371
+ }
372
+ },
373
+ };
374
+
375
+ const activityTrackingDefaultsModifier = {
376
+ entityName: '*',
377
+ order: 5,
378
+ modifier: (ctx) => {
379
+ const plugins = ctx.plugins.list();
380
+ if (plugins.some((p) => p.name === ACTIVITY_TRACKING_PLUGIN_NAME)) {
381
+ return;
382
+ }
383
+ ctx.plugins.add({
384
+ name: ACTIVITY_TRACKING_PLUGIN_NAME,
385
+ options: normalizeActivityTrackingPluginOptions({ enabled: true }),
386
+ });
387
+ },
388
+ };
389
+
390
+ const PRIORITY_ORDER = ['high', 'normal', 'low'];
391
+ const COALESCE_KEYS = new Set([
392
+ 'entity.filter.applied',
393
+ 'entity.search.applied',
394
+ 'entity.sort.applied',
395
+ 'form.field.changed',
396
+ ]);
397
+ const DEFAULT_DEBOUNCE_MS = {
398
+ 'entity.search.applied': 800,
399
+ 'entity.sort.applied': 300,
400
+ 'entity.filter.applied': 300,
401
+ 'form.field.changed': 500,
402
+ 'query.executed': 1500,
403
+ };
404
+ class AXMActivityBuffer {
405
+ constructor(config) {
406
+ this.config = config;
407
+ this.lanes = {
408
+ high: [],
409
+ normal: [],
410
+ low: [],
411
+ };
412
+ this.debounceTimers = new Map();
413
+ this.droppedCount = 0;
414
+ this.debounceMs = { ...DEFAULT_DEBOUNCE_MS, ...(config.debounceMs ?? {}) };
415
+ }
416
+ enqueue(event) {
417
+ const priority = event.priority ?? 'normal';
418
+ const queued = { ...event, priority };
419
+ const debounceKey = this.debounceMs[event.name];
420
+ if (debounceKey != null) {
421
+ this.scheduleDebounced(event.name, queued, debounceKey);
422
+ return;
423
+ }
424
+ this.push(queued);
425
+ }
426
+ drain(maxCount) {
427
+ const out = [];
428
+ for (const lane of PRIORITY_ORDER) {
429
+ while (this.lanes[lane].length && (maxCount == null || out.length < maxCount)) {
430
+ const next = this.lanes[lane].shift();
431
+ if (next) {
432
+ out.push(next);
433
+ }
434
+ }
435
+ }
436
+ return out;
437
+ }
438
+ peekCount() {
439
+ return PRIORITY_ORDER.reduce((sum, lane) => sum + this.lanes[lane].length, 0);
440
+ }
441
+ get dropped() {
442
+ return this.droppedCount;
443
+ }
444
+ clear() {
445
+ for (const lane of PRIORITY_ORDER) {
446
+ this.lanes[lane].length = 0;
447
+ }
448
+ for (const timer of this.debounceTimers.values()) {
449
+ clearTimeout(timer);
450
+ }
451
+ this.debounceTimers.clear();
452
+ }
453
+ requeueFront(events) {
454
+ for (let i = events.length - 1; i >= 0; i--) {
455
+ const event = events[i];
456
+ const priority = event.priority ?? 'normal';
457
+ this.lanes[priority].unshift({ ...event, priority });
458
+ }
459
+ }
460
+ scheduleDebounced(name, event, delayMs) {
461
+ const key = `${name}:${event.entityName ?? ''}:${event.route ?? ''}`;
462
+ const existing = this.debounceTimers.get(key);
463
+ if (existing) {
464
+ clearTimeout(existing);
465
+ }
466
+ const timer = setTimeout(() => {
467
+ this.debounceTimers.delete(key);
468
+ this.push(event);
469
+ }, delayMs);
470
+ this.debounceTimers.set(key, timer);
471
+ }
472
+ push(event) {
473
+ if (COALESCE_KEYS.has(event.name)) {
474
+ this.coalesce(event);
475
+ }
476
+ const priority = event.priority ?? 'normal';
477
+ this.lanes[priority].push(event);
478
+ this.enforceCapacity();
479
+ }
480
+ coalesce(event) {
481
+ const lane = this.lanes[event.priority ?? 'normal'];
482
+ const index = lane.findIndex((e) => e.name === event.name &&
483
+ e.entityName === event.entityName &&
484
+ e.route === event.route &&
485
+ e.viewId === event.viewId);
486
+ if (index >= 0) {
487
+ lane.splice(index, 1);
488
+ }
489
+ }
490
+ enforceCapacity() {
491
+ while (this.size() > this.config.maxQueueSize) {
492
+ if (this.lanes.low.length) {
493
+ this.lanes.low.shift();
494
+ this.droppedCount++;
495
+ continue;
496
+ }
497
+ if (this.coalesceOverflow()) {
498
+ continue;
499
+ }
500
+ if (this.lanes.normal.length) {
501
+ this.lanes.normal.shift();
502
+ this.droppedCount++;
503
+ continue;
504
+ }
505
+ break;
506
+ }
507
+ }
508
+ coalesceOverflow() {
509
+ let merged = false;
510
+ for (const name of COALESCE_KEYS) {
511
+ for (const lane of PRIORITY_ORDER) {
512
+ const duplicates = this.lanes[lane].filter((e) => e.name === name);
513
+ if (duplicates.length > 1) {
514
+ const keep = duplicates[duplicates.length - 1];
515
+ this.lanes[lane] = this.lanes[lane].filter((e) => e.name !== name || e === keep);
516
+ merged = true;
517
+ }
518
+ }
519
+ }
520
+ return merged;
521
+ }
522
+ size() {
523
+ return PRIORITY_ORDER.reduce((sum, lane) => sum + this.lanes[lane].length, 0);
524
+ }
525
+ }
526
+
527
+ const DEFAULT_QUERY_SAMPLING_RATE = 0.25;
528
+ const NOISE_BLOCKED_NAMES = new Set([
529
+ 'form.field.changed',
530
+ 'ui.hover',
531
+ 'ui.keypress',
532
+ 'ui.scroll',
533
+ ]);
534
+ class AXMActivityInsightActivityPolicyService {
535
+ constructor() {
536
+ this.settingsService = inject(AXPSettingsService);
537
+ this.enabledOverride = null;
538
+ this.entityPolicy = null;
539
+ }
540
+ setEntityPolicy(policy) {
541
+ this.entityPolicy = policy;
542
+ }
543
+ enable() {
544
+ this.enabledOverride = true;
545
+ }
546
+ disable() {
547
+ this.enabledOverride = false;
548
+ }
549
+ async isEnabled() {
550
+ if (this.enabledOverride != null) {
551
+ return this.enabledOverride;
552
+ }
553
+ try {
554
+ const setting = await this.settingsService.get(AXPActivityInsightSettings.Enabled);
555
+ if (setting === false) {
556
+ return false;
557
+ }
558
+ }
559
+ catch {
560
+ // settings unavailable — default to enabled
561
+ }
562
+ return this.entityPolicy?.enabled !== false;
563
+ }
564
+ async shouldTrack(event) {
565
+ if (!(await this.isEnabled())) {
566
+ return false;
567
+ }
568
+ if (NOISE_BLOCKED_NAMES.has(event.name)) {
569
+ return false;
570
+ }
571
+ if (this.entityPolicy?.enabled === false) {
572
+ return false;
573
+ }
574
+ if (!(await this.passesSampling(event))) {
575
+ return false;
576
+ }
577
+ return true;
578
+ }
579
+ async applyMasking(event) {
580
+ const maskingEnabled = await this.isMaskingEnabled();
581
+ if (!maskingEnabled) {
582
+ return event;
583
+ }
584
+ const policy = {
585
+ enabled: true,
586
+ denylist: this.entityPolicy?.masking?.fields,
587
+ maxPropertyBytes: AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.maxPropertyBytes,
588
+ };
589
+ const properties = event.properties
590
+ ? maskActivityProperties(event.properties, policy)
591
+ : undefined;
592
+ const metadata = event.metadata
593
+ ? maskActivityProperties(event.metadata, policy)
594
+ : undefined;
595
+ return {
596
+ ...event,
597
+ properties: truncateActivityProperties(properties, policy.maxPropertyBytes ?? 4000),
598
+ metadata: truncateActivityProperties(metadata, policy.maxPropertyBytes ?? 4000),
599
+ };
600
+ }
601
+ defaultPriorityFor(eventName) {
602
+ if (eventName === AXMActivityEventNames.Query.Executed) {
603
+ return 'low';
604
+ }
605
+ if (eventName === AXMActivityEventNames.Error.Seen) {
606
+ return 'high';
607
+ }
608
+ return 'normal';
609
+ }
610
+ async isMaskingEnabled() {
611
+ try {
612
+ return (await this.settingsService.get(AXPActivityInsightSettings.MaskingEnabled)) !== false;
613
+ }
614
+ catch {
615
+ return true;
616
+ }
617
+ }
618
+ async passesSampling(event) {
619
+ try {
620
+ const samplingEnabled = await this.settingsService.get(AXPActivityInsightSettings.SamplingEnabled);
621
+ if (samplingEnabled === false) {
622
+ return true;
623
+ }
624
+ }
625
+ catch {
626
+ // default sampling on
627
+ }
628
+ const entityRate = this.entityPolicy?.sampling?.[event.name];
629
+ const rate = entityRate ??
630
+ (event.name === AXMActivityEventNames.Query.Executed ? DEFAULT_QUERY_SAMPLING_RATE : 1);
631
+ if (rate >= 1) {
632
+ return true;
633
+ }
634
+ if (rate <= 0) {
635
+ return false;
636
+ }
637
+ return Math.random() < rate;
638
+ }
639
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityPolicyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
640
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityPolicyService, providedIn: 'root' }); }
641
+ }
642
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityPolicyService, decorators: [{
643
+ type: Injectable,
644
+ args: [{ providedIn: 'root' }]
645
+ }] });
646
+
647
+ class AXMActivityInsightActivityTransport {
648
+ constructor() {
649
+ this.transportService = inject(AXPRuntimeTransportService);
650
+ this.retryAttempt = 0;
651
+ }
652
+ async send(events, options = {}) {
653
+ if (!events.length) {
654
+ return true;
655
+ }
656
+ const maxBytes = options.maxPayloadBytes ??
657
+ (options.final
658
+ ? AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.finalFlushMaxBytes
659
+ : AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.maxPayloadBytes);
660
+ const batch = this.buildBatch(this.trimToPayload(events, maxBytes));
661
+ try {
662
+ const result = await this.transportService.execute(AXMActivityInsightTransportKeys.Event.BulkIngest, {
663
+ batch,
664
+ final: options.final,
665
+ useBeacon: Boolean(options.final && AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.useBeaconForFinalFlush),
666
+ });
667
+ if (result.accepted) {
668
+ this.retryAttempt = 0;
669
+ return true;
670
+ }
671
+ return false;
672
+ }
673
+ catch (err) {
674
+ console.debug('[ActivityInsight] transport failed', err);
675
+ return false;
676
+ }
677
+ }
678
+ nextRetryDelayMs() {
679
+ const { retryBackoffMs, retryLimit } = AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG;
680
+ if (this.retryAttempt >= retryLimit) {
681
+ return -1;
682
+ }
683
+ const delay = retryBackoffMs * 2 ** this.retryAttempt;
684
+ this.retryAttempt++;
685
+ return delay;
686
+ }
687
+ resetRetry() {
688
+ this.retryAttempt = 0;
689
+ }
690
+ buildBatch(events) {
691
+ return {
692
+ batchId: crypto.randomUUID(),
693
+ sentAt: new Date().toISOString(),
694
+ events,
695
+ };
696
+ }
697
+ trimToPayload(events, maxBytes) {
698
+ const sorted = [...events].sort((a, b) => {
699
+ const rank = (p) => (p === 'high' ? 0 : p === 'normal' ? 1 : 2);
700
+ return rank(a.priority) - rank(b.priority);
701
+ });
702
+ const out = [];
703
+ for (const event of sorted) {
704
+ const candidate = [...out, event];
705
+ if (JSON.stringify(this.buildBatch(candidate)).length <= maxBytes) {
706
+ out.push(event);
707
+ }
708
+ }
709
+ return out.length ? out : sorted.slice(0, 1);
710
+ }
711
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityTransport, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
712
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityTransport, providedIn: 'root' }); }
713
+ }
714
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightActivityTransport, decorators: [{
715
+ type: Injectable,
716
+ args: [{ providedIn: 'root' }]
717
+ }] });
718
+
719
+ class AXMActivityInsightJourneyService {
720
+ constructor() {
721
+ this.activities = new Map();
722
+ this.journeys = new Map();
723
+ }
724
+ startJourney(name, sessionId) {
725
+ const journey = {
726
+ id: crypto.randomUUID(),
727
+ name,
728
+ sessionId,
729
+ startedAt: new Date().toISOString(),
730
+ };
731
+ this.journeys.set(journey.id, journey);
732
+ return { ...journey, status: 'started' };
733
+ }
734
+ endJourney(journeyId, status = 'completed', result) {
735
+ const journey = this.journeys.get(journeyId);
736
+ if (!journey) {
737
+ return null;
738
+ }
739
+ this.journeys.delete(journeyId);
740
+ return {
741
+ ...journey,
742
+ endedAt: new Date().toISOString(),
743
+ status,
744
+ result,
745
+ };
746
+ }
747
+ getActiveJourney(journeyId) {
748
+ return this.journeys.get(journeyId);
749
+ }
750
+ startActivity(name, options) {
751
+ const activity = {
752
+ id: crypto.randomUUID(),
753
+ name,
754
+ startedAt: new Date().toISOString(),
755
+ parentActivityId: options?.parentActivityId,
756
+ journeyId: options?.journeyId,
757
+ properties: options?.properties,
758
+ };
759
+ this.activities.set(activity.id, activity);
760
+ return activity;
761
+ }
762
+ endActivity(activityId, status = 'completed', properties) {
763
+ const activity = this.activities.get(activityId);
764
+ if (!activity) {
765
+ return null;
766
+ }
767
+ this.activities.delete(activityId);
768
+ const endedAt = new Date().toISOString();
769
+ const durationMs = Date.parse(endedAt) - Date.parse(activity.startedAt);
770
+ return {
771
+ ...activity,
772
+ endedAt,
773
+ status,
774
+ durationMs,
775
+ properties: { ...activity.properties, ...properties },
776
+ };
777
+ }
778
+ buildActivityStartedEvent(activity, enrich) {
779
+ return {
780
+ id: activity.id,
781
+ name: activity.name,
782
+ status: 'started',
783
+ timestamp: activity.startedAt,
784
+ parentActivityId: activity.parentActivityId,
785
+ journeyId: activity.journeyId,
786
+ properties: activity.properties,
787
+ ...enrich,
788
+ };
789
+ }
790
+ buildActivityEndedEvent(activity, enrich) {
791
+ return {
792
+ id: activity.id,
793
+ name: activity.name,
794
+ status: activity.status,
795
+ timestamp: activity.endedAt,
796
+ durationMs: activity.durationMs,
797
+ parentActivityId: activity.parentActivityId,
798
+ journeyId: activity.journeyId,
799
+ properties: activity.properties,
800
+ ...enrich,
801
+ };
802
+ }
803
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightJourneyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
804
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightJourneyService, providedIn: 'root' }); }
805
+ }
806
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightJourneyService, decorators: [{
807
+ type: Injectable,
808
+ args: [{ providedIn: 'root' }]
809
+ }] });
810
+
811
+ const ACTIVITY_TRACK_HOOK_KEY = 'activity:track';
812
+ const ACTIVITY_BEFORE_SEND_HOOK_KEY = 'activity-insight.before-send';
813
+ const SESSION_STORAGE_KEY = 'axm-activity-session-id';
814
+ const MIN_FLUSH_INTERVAL_MS = 1_000;
815
+ const MAX_FLUSH_INTERVAL_MS = 60_000;
816
+ const MIN_BATCH_SIZE = 1;
817
+ const MAX_BATCH_SIZE = 500;
818
+ class AXMActivityInsightTrackerService {
819
+ constructor() {
820
+ this.sessionService = inject(AXPSessionService);
821
+ this.settingsService = inject(AXPSettingsService);
822
+ this.hooks = inject(AXPHookService);
823
+ this.policy = inject(AXMActivityInsightActivityPolicyService);
824
+ this.transport = inject(AXMActivityInsightActivityTransport);
825
+ this.journeyService = inject(AXMActivityInsightJourneyService);
826
+ this.router = inject(Router);
827
+ this.destroyRef = inject(DestroyRef);
828
+ this.buffer = new AXMActivityBuffer({
829
+ maxQueueSize: AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.maxQueueSize,
830
+ });
831
+ this.flushTimer = null;
832
+ this.flushTimerStarting = false;
833
+ this.flushChain = Promise.resolve();
834
+ this.pendingRetry = null;
835
+ this.initialized = false;
836
+ this.visibilityHandlersRegistered = false;
837
+ this.currentJourneyId = null;
838
+ }
839
+ init() {
840
+ if (this.initialized || typeof window === 'undefined') {
841
+ return;
842
+ }
843
+ this.initialized = true;
844
+ this.sessionService.isReadyForAppScopedApi$
845
+ .pipe(distinctUntilChanged())
846
+ .subscribe((ready) => {
847
+ if (ready) {
848
+ this.onSessionActive();
849
+ }
850
+ else {
851
+ this.onSessionInactive();
852
+ }
853
+ });
854
+ if (!this.visibilityHandlersRegistered) {
855
+ this.registerVisibilityFlush();
856
+ this.visibilityHandlersRegistered = true;
857
+ }
858
+ this.destroyRef.onDestroy(() => {
859
+ void this.flush('final');
860
+ this.onSessionInactive();
861
+ });
862
+ }
863
+ async track(name, payload = {}) {
864
+ await this.trackMany([{ name, ...payload }]);
865
+ }
866
+ async trackMany(events) {
867
+ if (!this.canTrack()) {
868
+ return;
869
+ }
870
+ for (const partial of events) {
871
+ try {
872
+ const built = await this.buildEvent(partial);
873
+ if (!(await this.policy.shouldTrack(built))) {
874
+ continue;
875
+ }
876
+ const masked = await this.policy.applyMasking(built);
877
+ const finalEvent = await this.hooks.runAsync(ACTIVITY_BEFORE_SEND_HOOK_KEY, masked);
878
+ if (!finalEvent) {
879
+ continue;
880
+ }
881
+ this.buffer.enqueue(finalEvent);
882
+ }
883
+ catch (err) {
884
+ console.debug('[ActivityInsight] track skipped', err);
885
+ }
886
+ }
887
+ await this.flushIfNeeded();
888
+ }
889
+ startJourney(name) {
890
+ const journey = this.journeyService.startJourney(name, this.ensureSessionId());
891
+ this.currentJourneyId = journey.id;
892
+ void this.track(name, {
893
+ type: 'system',
894
+ category: 'custom',
895
+ journeyId: journey.id,
896
+ status: 'started',
897
+ });
898
+ return journey;
899
+ }
900
+ endJourney(journeyId, status = 'completed', result) {
901
+ const id = journeyId ?? this.currentJourneyId;
902
+ if (!id) {
903
+ return;
904
+ }
905
+ const journey = this.journeyService.endJourney(id, status, result);
906
+ if (!journey) {
907
+ return;
908
+ }
909
+ if (this.currentJourneyId === id) {
910
+ this.currentJourneyId = null;
911
+ }
912
+ void this.track(journey.name, {
913
+ type: 'system',
914
+ category: 'custom',
915
+ journeyId: journey.id,
916
+ status,
917
+ properties: result,
918
+ });
919
+ void this.flush('manual');
920
+ }
921
+ startActivity(name, properties) {
922
+ const activity = this.journeyService.startActivity(name, {
923
+ journeyId: this.currentJourneyId ?? undefined,
924
+ properties,
925
+ });
926
+ void this.track(name, {
927
+ id: activity.id,
928
+ type: 'action',
929
+ category: 'custom',
930
+ status: 'started',
931
+ parentActivityId: activity.parentActivityId,
932
+ journeyId: activity.journeyId,
933
+ properties,
934
+ });
935
+ return activity.id;
936
+ }
937
+ endActivity(activityId, status = 'completed', properties) {
938
+ const ended = this.journeyService.endActivity(activityId, status, properties);
939
+ if (!ended) {
940
+ return;
941
+ }
942
+ void this.track(ended.name, {
943
+ id: ended.id,
944
+ type: 'action',
945
+ category: 'custom',
946
+ status: ended.status,
947
+ durationMs: ended.durationMs,
948
+ parentActivityId: ended.parentActivityId,
949
+ journeyId: ended.journeyId,
950
+ properties: ended.properties,
951
+ });
952
+ }
953
+ trackPageView(route) {
954
+ void this.track(AXMActivityEventNames.Page.Viewed, {
955
+ type: 'view',
956
+ category: 'navigation',
957
+ route: route ?? this.router.url,
958
+ });
959
+ }
960
+ trackClick(name, properties) {
961
+ void this.track(name, { type: 'action', category: 'custom', properties });
962
+ }
963
+ trackEntityListOpen(module, entityName, properties) {
964
+ void this.track(AXMActivityEventNames.Entity.ListOpened, {
965
+ type: 'view',
966
+ category: 'entity',
967
+ module,
968
+ entityName,
969
+ properties,
970
+ });
971
+ }
972
+ trackEntityDetailOpen(module, entityName, entityId, properties) {
973
+ void this.track(AXMActivityEventNames.Entity.DetailOpened, {
974
+ type: 'view',
975
+ category: 'entity',
976
+ module,
977
+ entityName,
978
+ entityId,
979
+ properties,
980
+ });
981
+ }
982
+ trackFilterApply(entityName, properties) {
983
+ void this.track(AXMActivityEventNames.Entity.FilterApplied, {
984
+ type: 'action',
985
+ category: 'entity',
986
+ entityName,
987
+ properties,
988
+ priority: 'low',
989
+ });
990
+ }
991
+ trackSearch(entityName, properties) {
992
+ void this.track(AXMActivityEventNames.Entity.SearchApplied, {
993
+ type: 'action',
994
+ category: 'entity',
995
+ entityName,
996
+ properties,
997
+ priority: 'low',
998
+ });
999
+ }
1000
+ trackSort(entityName, properties) {
1001
+ void this.track(AXMActivityEventNames.Entity.SortApplied, {
1002
+ type: 'action',
1003
+ category: 'entity',
1004
+ entityName,
1005
+ properties,
1006
+ priority: 'low',
1007
+ });
1008
+ }
1009
+ trackCommandExecution(commandName, options) {
1010
+ void this.track(AXMActivityEventNames.Command.Executed, {
1011
+ type: 'action',
1012
+ category: 'command',
1013
+ commandName,
1014
+ status: options?.success === false ? 'failed' : 'completed',
1015
+ durationMs: options?.durationMs,
1016
+ properties: options?.properties,
1017
+ });
1018
+ }
1019
+ trackQueryExecution(queryName, options) {
1020
+ void this.track(AXMActivityEventNames.Query.Executed, {
1021
+ type: 'system',
1022
+ category: 'query',
1023
+ queryName,
1024
+ status: options?.success === false ? 'failed' : 'completed',
1025
+ durationMs: options?.durationMs,
1026
+ properties: options?.properties,
1027
+ priority: 'low',
1028
+ });
1029
+ }
1030
+ trackFormSubmit(entityName, properties) {
1031
+ void this.track(AXMActivityEventNames.Form.Submitted, {
1032
+ type: 'action',
1033
+ category: 'form',
1034
+ entityName,
1035
+ properties,
1036
+ });
1037
+ }
1038
+ trackValidationError(entityName, properties) {
1039
+ void this.track(AXMActivityEventNames.Form.ValidationFailed, {
1040
+ type: 'error',
1041
+ category: 'form',
1042
+ entityName,
1043
+ properties,
1044
+ severity: 'normal',
1045
+ });
1046
+ }
1047
+ trackAiInteraction(name, properties) {
1048
+ void this.track(name, { type: 'action', category: 'ai', properties });
1049
+ }
1050
+ trackError(error, properties) {
1051
+ const message = error instanceof Error ? error.message : String(error);
1052
+ void this.track(AXMActivityEventNames.Error.Seen, {
1053
+ type: 'error',
1054
+ category: 'error',
1055
+ severity: 'high',
1056
+ priority: 'high',
1057
+ properties: { message, ...properties },
1058
+ });
1059
+ }
1060
+ async flush(reason = 'manual') {
1061
+ if (!this.canTrack()) {
1062
+ return;
1063
+ }
1064
+ this.flushChain = this.flushChain.then(async () => {
1065
+ const events = this.buffer.drain();
1066
+ if (!events.length) {
1067
+ return;
1068
+ }
1069
+ const sent = await this.transport.send(events, { final: reason === 'final' });
1070
+ if (!sent) {
1071
+ this.buffer.requeueFront(events);
1072
+ this.scheduleRetry();
1073
+ return;
1074
+ }
1075
+ this.transport.resetRetry();
1076
+ });
1077
+ await this.flushChain;
1078
+ }
1079
+ enable() {
1080
+ this.policy.enable();
1081
+ }
1082
+ disable() {
1083
+ this.policy.disable();
1084
+ }
1085
+ getSessionId() {
1086
+ return this.ensureSessionId();
1087
+ }
1088
+ canTrack() {
1089
+ return Boolean(this.sessionService.getToken());
1090
+ }
1091
+ onSessionActive() {
1092
+ this.ensureSessionId();
1093
+ this.startFlushTimer();
1094
+ }
1095
+ onSessionInactive() {
1096
+ this.stopFlushTimer();
1097
+ this.cancelPendingRetry();
1098
+ this.buffer.clear();
1099
+ this.transport.resetRetry();
1100
+ this.currentJourneyId = null;
1101
+ }
1102
+ cancelPendingRetry() {
1103
+ if (this.pendingRetry) {
1104
+ clearTimeout(this.pendingRetry);
1105
+ this.pendingRetry = null;
1106
+ }
1107
+ }
1108
+ async buildEvent(partial) {
1109
+ const session = this.sessionService;
1110
+ const priority = partial.priority ?? this.policy.defaultPriorityFor(partial.name);
1111
+ return {
1112
+ id: partial.id ?? crypto.randomUUID(),
1113
+ name: partial.name,
1114
+ type: partial.type ?? this.inferType(partial.name),
1115
+ category: partial.category ?? this.inferCategory(partial.name),
1116
+ timestamp: partial.timestamp ?? new Date().toISOString(),
1117
+ sessionId: partial.sessionId ?? this.ensureSessionId(),
1118
+ journeyId: partial.journeyId ?? this.currentJourneyId ?? undefined,
1119
+ parentActivityId: partial.parentActivityId,
1120
+ userId: partial.userId ?? session.user?.id,
1121
+ username: partial.username ?? session.user?.name,
1122
+ tenantId: partial.tenantId ?? session.tenant?.id,
1123
+ applicationId: partial.applicationId ?? session.application?.id,
1124
+ source: partial.source ?? 'frontend',
1125
+ route: partial.route ?? this.router.url,
1126
+ module: partial.module,
1127
+ entityName: partial.entityName,
1128
+ entityId: partial.entityId,
1129
+ viewId: partial.viewId,
1130
+ widgetId: partial.widgetId,
1131
+ commandName: partial.commandName,
1132
+ queryName: partial.queryName,
1133
+ workflowId: partial.workflowId,
1134
+ aiConversationId: partial.aiConversationId,
1135
+ status: partial.status,
1136
+ durationMs: partial.durationMs,
1137
+ severity: partial.severity,
1138
+ priority,
1139
+ properties: partial.properties,
1140
+ metadata: partial.metadata ?? { sdkVersion: '1.0.0' },
1141
+ };
1142
+ }
1143
+ inferType(name) {
1144
+ if (name.startsWith('error.') || name.includes('validation')) {
1145
+ return 'error';
1146
+ }
1147
+ if (name.startsWith('page.') || name.includes('.opened')) {
1148
+ return 'view';
1149
+ }
1150
+ if (name.startsWith('query.')) {
1151
+ return 'system';
1152
+ }
1153
+ return 'action';
1154
+ }
1155
+ inferCategory(name) {
1156
+ if (name.startsWith('page.'))
1157
+ return 'navigation';
1158
+ if (name.startsWith('entity.'))
1159
+ return 'entity';
1160
+ if (name.startsWith('command.'))
1161
+ return 'command';
1162
+ if (name.startsWith('query.'))
1163
+ return 'query';
1164
+ if (name.startsWith('form.'))
1165
+ return 'form';
1166
+ if (name.startsWith('ai.'))
1167
+ return 'ai';
1168
+ if (name.startsWith('workflow.'))
1169
+ return 'workflow';
1170
+ if (name.startsWith('security.'))
1171
+ return 'security';
1172
+ if (name.startsWith('error.'))
1173
+ return 'error';
1174
+ return 'custom';
1175
+ }
1176
+ ensureSessionId() {
1177
+ try {
1178
+ let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
1179
+ if (!id) {
1180
+ id = crypto.randomUUID();
1181
+ sessionStorage.setItem(SESSION_STORAGE_KEY, id);
1182
+ }
1183
+ return id;
1184
+ }
1185
+ catch {
1186
+ return crypto.randomUUID();
1187
+ }
1188
+ }
1189
+ async flushIfNeeded() {
1190
+ const maxBatchSize = await this.getBufferSetting(AXPActivityInsightSettings.MaxBatchSize, AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.maxBatchSize, MIN_BATCH_SIZE, MAX_BATCH_SIZE);
1191
+ if (this.buffer.peekCount() >= maxBatchSize) {
1192
+ await this.flush('batch-size');
1193
+ }
1194
+ }
1195
+ startFlushTimer() {
1196
+ if (this.flushTimer || this.flushTimerStarting) {
1197
+ return;
1198
+ }
1199
+ this.flushTimerStarting = true;
1200
+ void this.getBufferSetting(AXPActivityInsightSettings.FlushIntervalMs, AXM_DEFAULT_ACTIVITY_BUFFER_CONFIG.flushIntervalMs, MIN_FLUSH_INTERVAL_MS, MAX_FLUSH_INTERVAL_MS)
1201
+ .then((ms) => {
1202
+ if (this.flushTimer) {
1203
+ return;
1204
+ }
1205
+ this.flushTimer = setInterval(() => void this.flush('interval'), ms);
1206
+ })
1207
+ .finally(() => {
1208
+ this.flushTimerStarting = false;
1209
+ });
1210
+ }
1211
+ stopFlushTimer() {
1212
+ if (this.flushTimer) {
1213
+ clearInterval(this.flushTimer);
1214
+ this.flushTimer = null;
1215
+ }
1216
+ this.flushTimerStarting = false;
1217
+ }
1218
+ registerVisibilityFlush() {
1219
+ if (typeof document === 'undefined') {
1220
+ return;
1221
+ }
1222
+ const handler = () => {
1223
+ if (document.visibilityState === 'hidden') {
1224
+ void this.flush('visibility');
1225
+ }
1226
+ };
1227
+ document.addEventListener('visibilitychange', handler);
1228
+ window.addEventListener('pagehide', () => void this.flush('final'));
1229
+ this.destroyRef.onDestroy(() => {
1230
+ document.removeEventListener('visibilitychange', handler);
1231
+ });
1232
+ }
1233
+ scheduleRetry() {
1234
+ const delay = this.transport.nextRetryDelayMs();
1235
+ if (delay < 0 || this.pendingRetry) {
1236
+ return;
1237
+ }
1238
+ this.pendingRetry = setTimeout(() => {
1239
+ this.pendingRetry = null;
1240
+ void this.flush('manual');
1241
+ }, delay);
1242
+ }
1243
+ async getSetting(key, fallback) {
1244
+ try {
1245
+ const value = await this.settingsService.get(key);
1246
+ return value ?? fallback;
1247
+ }
1248
+ catch {
1249
+ return fallback;
1250
+ }
1251
+ }
1252
+ async getBufferSetting(key, fallback, min, max) {
1253
+ try {
1254
+ const value = await this.settingsService.get(key);
1255
+ return this.clampBufferNumber(value, fallback, min, max);
1256
+ }
1257
+ catch {
1258
+ return fallback;
1259
+ }
1260
+ }
1261
+ clampBufferNumber(value, fallback, min, max) {
1262
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
1263
+ return fallback;
1264
+ }
1265
+ return Math.min(max, Math.max(min, Math.round(value)));
1266
+ }
1267
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1268
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightTrackerService, providedIn: 'root' }); }
1269
+ }
1270
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightTrackerService, decorators: [{
1271
+ type: Injectable,
1272
+ args: [{ providedIn: 'root' }]
1273
+ }] });
1274
+
1275
+ class AXMActivityInsightPageViewInstrumentation {
1276
+ constructor() {
1277
+ this.router = inject(Router);
1278
+ this.tracker = inject(AXMActivityInsightTrackerService);
1279
+ }
1280
+ init() {
1281
+ this.router.events.pipe(filter((e) => e instanceof NavigationEnd)).subscribe((event) => {
1282
+ this.tracker.trackPageView(event.urlAfterRedirects);
1283
+ });
1284
+ }
1285
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPageViewInstrumentation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1286
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPageViewInstrumentation, providedIn: 'root' }); }
1287
+ }
1288
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightPageViewInstrumentation, decorators: [{
1289
+ type: Injectable,
1290
+ args: [{ providedIn: 'root' }]
1291
+ }] });
1292
+
1293
+ class AXMActivityInsightCommandInstrumentation {
1294
+ constructor() {
1295
+ this.commandEvents = inject(AXPCommandEventsService);
1296
+ this.tracker = inject(AXMActivityInsightTrackerService);
1297
+ this.destroyRef = inject(DestroyRef);
1298
+ }
1299
+ init() {
1300
+ const unsubscribe = this.commandEvents.listen((event) => this.handle(event));
1301
+ this.destroyRef.onDestroy(unsubscribe);
1302
+ }
1303
+ handle(event) {
1304
+ if (event.stage !== 'after') {
1305
+ return;
1306
+ }
1307
+ this.tracker.trackCommandExecution(event.key, {
1308
+ success: event.success,
1309
+ durationMs: event.durationMs,
1310
+ properties: {
1311
+ executionId: event.executionId,
1312
+ input: event.input,
1313
+ error: event.error,
1314
+ },
1315
+ });
1316
+ }
1317
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightCommandInstrumentation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1318
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightCommandInstrumentation, providedIn: 'root' }); }
1319
+ }
1320
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightCommandInstrumentation, decorators: [{
1321
+ type: Injectable,
1322
+ args: [{ providedIn: 'root' }]
1323
+ }] });
1324
+
1325
+ const QUERY_EXECUTION_EVENT_KEY = '@queries:execution';
1326
+ class AXMActivityInsightQueryInstrumentation {
1327
+ constructor() {
1328
+ this.broadcast = inject(AXPBroadcastEventService);
1329
+ this.tracker = inject(AXMActivityInsightTrackerService);
1330
+ this.destroyRef = inject(DestroyRef);
1331
+ }
1332
+ init() {
1333
+ const subscription = this.broadcast
1334
+ .listen(QUERY_EXECUTION_EVENT_KEY)
1335
+ .subscribe(({ data }) => this.handle(data));
1336
+ this.destroyRef.onDestroy(() => subscription.unsubscribe());
1337
+ }
1338
+ handle(event) {
1339
+ if (event.stage !== 'after') {
1340
+ return;
1341
+ }
1342
+ this.tracker.trackQueryExecution(event.key, {
1343
+ success: event.success,
1344
+ durationMs: event.durationMs,
1345
+ properties: {
1346
+ executionId: event.executionId,
1347
+ input: event.input,
1348
+ error: event.error,
1349
+ },
1350
+ });
1351
+ }
1352
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightQueryInstrumentation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1353
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightQueryInstrumentation, providedIn: 'root' }); }
1354
+ }
1355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightQueryInstrumentation, decorators: [{
1356
+ type: Injectable,
1357
+ args: [{ providedIn: 'root' }]
1358
+ }] });
1359
+
1360
+ const EntityLifecycleHookKeys = {
1361
+ ListOpened: 'entity.list.opened',
1362
+ ListCriteriaApplied: 'entity.list.criteria-applied',
1363
+ DetailOpened: 'entity.detail.opened',
1364
+ FormSubmitted: 'entity.form.submitted',
1365
+ FormValidationFailed: 'entity.form.validation-failed',
1366
+ };
1367
+ class AXMActivityInsightEntityInstrumentation {
1368
+ constructor() {
1369
+ this.tracker = inject(AXMActivityInsightTrackerService);
1370
+ }
1371
+ init() {
1372
+ // Hook providers are registered via module; this ensures tracker is ready.
1373
+ }
1374
+ handleListOpened(data) {
1375
+ const module = String(data['module'] ?? data['moduleName'] ?? '');
1376
+ const entityName = String(data['entityName'] ?? data['entity'] ?? '');
1377
+ this.tracker.trackEntityListOpen(module, entityName, data);
1378
+ }
1379
+ handleDetailOpened(data) {
1380
+ const module = String(data['module'] ?? data['moduleName'] ?? '');
1381
+ const entityName = String(data['entityName'] ?? data['entity'] ?? '');
1382
+ const entityId = data['entityId'] != null ? String(data['entityId']) : undefined;
1383
+ this.tracker.trackEntityDetailOpen(module, entityName, entityId, data);
1384
+ }
1385
+ handleCriteriaApplied(data) {
1386
+ const entityName = String(data['entityName'] ?? data['entity'] ?? '');
1387
+ const kind = String(data['kind'] ?? data['deltaType'] ?? 'filter');
1388
+ if (kind === 'search') {
1389
+ this.tracker.trackSearch(entityName, data);
1390
+ return;
1391
+ }
1392
+ if (kind === 'sort') {
1393
+ this.tracker.trackSort(entityName, data);
1394
+ return;
1395
+ }
1396
+ this.tracker.trackFilterApply(entityName, data);
1397
+ }
1398
+ handleFormSubmitted(data) {
1399
+ const entityName = String(data['entityName'] ?? data['entity'] ?? '');
1400
+ this.tracker.trackFormSubmit(entityName, data);
1401
+ }
1402
+ handleValidationFailed(data) {
1403
+ const entityName = String(data['entityName'] ?? data['entity'] ?? '');
1404
+ this.tracker.trackValidationError(entityName, data);
1405
+ }
1406
+ handleEntityCrud(data) {
1407
+ const operation = String(data['operation'] ?? '');
1408
+ const entityName = String(data['entityName'] ?? '');
1409
+ const nameMap = {
1410
+ inserted: AXMActivityEventNames.Entity.Created,
1411
+ updated: AXMActivityEventNames.Entity.Updated,
1412
+ deleted: AXMActivityEventNames.Entity.Deleted,
1413
+ };
1414
+ const name = nameMap[operation];
1415
+ if (!name) {
1416
+ return;
1417
+ }
1418
+ void this.tracker.track(name, {
1419
+ type: 'action',
1420
+ category: 'entity',
1421
+ entityName,
1422
+ entityId: data['entityId'] != null ? String(data['entityId']) : undefined,
1423
+ properties: data,
1424
+ });
1425
+ }
1426
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightEntityInstrumentation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1427
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightEntityInstrumentation, providedIn: 'root' }); }
1428
+ }
1429
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightEntityInstrumentation, decorators: [{
1430
+ type: Injectable,
1431
+ args: [{ providedIn: 'root' }]
1432
+ }] });
1433
+ function createHookProvider(key, handler) {
1434
+ return {
1435
+ key,
1436
+ priority: 50,
1437
+ execute: (data) => {
1438
+ const svc = inject(AXMActivityInsightEntityInstrumentation);
1439
+ handler(svc, data);
1440
+ },
1441
+ };
1442
+ }
1443
+ const AXMActivityInsightEntityHookProviders = [
1444
+ createHookProvider(EntityLifecycleHookKeys.ListOpened, (svc, data) => svc.handleListOpened(data)),
1445
+ createHookProvider(EntityLifecycleHookKeys.DetailOpened, (svc, data) => svc.handleDetailOpened(data)),
1446
+ createHookProvider(EntityLifecycleHookKeys.ListCriteriaApplied, (svc, data) => svc.handleCriteriaApplied(data)),
1447
+ createHookProvider(EntityLifecycleHookKeys.FormSubmitted, (svc, data) => svc.handleFormSubmitted(data)),
1448
+ createHookProvider(EntityLifecycleHookKeys.FormValidationFailed, (svc, data) => svc.handleValidationFailed(data)),
1449
+ createHookProvider('entity.inserted', (svc, data) => svc.handleEntityCrud({ ...data, operation: 'inserted' })),
1450
+ createHookProvider('entity.updated', (svc, data) => svc.handleEntityCrud({ ...data, operation: 'updated' })),
1451
+ createHookProvider('entity.deleted', (svc, data) => svc.handleEntityCrud({ ...data, operation: 'deleted' })),
1452
+ ];
1453
+
1454
+ class AXMActivityInsightErrorHandler {
1455
+ constructor() {
1456
+ this.tracker = inject(AXMActivityInsightTrackerService);
1457
+ }
1458
+ handleError(error, next) {
1459
+ try {
1460
+ this.tracker.trackError(error);
1461
+ }
1462
+ catch {
1463
+ // tracking must never break error handling
1464
+ }
1465
+ next(error);
1466
+ }
1467
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightErrorHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1468
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightErrorHandler }); }
1469
+ }
1470
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightErrorHandler, decorators: [{
1471
+ type: Injectable
1472
+ }] });
1473
+
1474
+ class AXMActivityInsightModule {
1475
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1476
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightModule, imports: [AXPRuntimeModule, i1.AXPCommonModule] }); }
1477
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightModule, providers: [
1478
+ {
1479
+ provide: ROUTES,
1480
+ multi: true,
1481
+ useFactory: () => activityInsightFeatureRoutes,
1482
+ },
1483
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: ActivityInsightManifest, multi: true },
1484
+ {
1485
+ provide: AXP_PERMISSION_DEFINITION_PROVIDER,
1486
+ useClass: AXMActivityInsightPermissionDefinitionProvider,
1487
+ multi: true,
1488
+ },
1489
+ { provide: AXP_MENU_PROVIDER, useClass: AXMActivityInsightMenuProvider, multi: true },
1490
+ { provide: AXP_SETTING_DEFINITION_PROVIDER, useClass: AXPActivityInsightSettingProvider, multi: true },
1491
+ { provide: AXP_SEARCH_PROVIDER, useClass: AXMActivityInsightSearchCommandProvider, multi: true },
1492
+ { provide: AXP_ENTITY_ACTION_PLUGIN, useValue: activityTrackingPlugin, multi: true },
1493
+ { provide: AXP_ENTITY_MODIFIER, useValue: activityTrackingDefaultsModifier, multi: true },
1494
+ ...AXMActivityInsightEntityHookProviders.map((provider) => ({
1495
+ provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
1496
+ useValue: provider,
1497
+ multi: true,
1498
+ })),
1499
+ {
1500
+ provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
1501
+ multi: true,
1502
+ useFactory: () => ({
1503
+ key: ACTIVITY_TRACK_HOOK_KEY,
1504
+ priority: 0,
1505
+ execute: (data) => {
1506
+ const tracker = inject(AXMActivityInsightTrackerService);
1507
+ if (data?.name) {
1508
+ void tracker.trackMany([data]);
1509
+ }
1510
+ },
1511
+ }),
1512
+ },
1513
+ provideAppStartUpTask(() => {
1514
+ const tracker = inject(AXMActivityInsightTrackerService);
1515
+ const pageView = inject(AXMActivityInsightPageViewInstrumentation);
1516
+ const command = inject(AXMActivityInsightCommandInstrumentation);
1517
+ const query = inject(AXMActivityInsightQueryInstrumentation);
1518
+ const entity = inject(AXMActivityInsightEntityInstrumentation);
1519
+ return {
1520
+ name: 'activity-insight-instrumentation',
1521
+ priority: AXP_APP_STARTUP_PRIORITY.activityInsight,
1522
+ run: async () => {
1523
+ tracker.init();
1524
+ pageView.init();
1525
+ command.init();
1526
+ query.init();
1527
+ entity.init();
1528
+ },
1529
+ };
1530
+ }),
1531
+ ], imports: [AXPRuntimeModule,
1532
+ AXPCommonModule.forChild({ errorHandlers: [AXMActivityInsightErrorHandler] })] }); }
1533
+ }
1534
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMActivityInsightModule, decorators: [{
1535
+ type: NgModule,
1536
+ args: [{
1537
+ imports: [
1538
+ AXPRuntimeModule,
1539
+ AXPCommonModule.forChild({ errorHandlers: [AXMActivityInsightErrorHandler] }),
1540
+ ],
1541
+ providers: [
1542
+ {
1543
+ provide: ROUTES,
1544
+ multi: true,
1545
+ useFactory: () => activityInsightFeatureRoutes,
1546
+ },
1547
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: ActivityInsightManifest, multi: true },
1548
+ {
1549
+ provide: AXP_PERMISSION_DEFINITION_PROVIDER,
1550
+ useClass: AXMActivityInsightPermissionDefinitionProvider,
1551
+ multi: true,
1552
+ },
1553
+ { provide: AXP_MENU_PROVIDER, useClass: AXMActivityInsightMenuProvider, multi: true },
1554
+ { provide: AXP_SETTING_DEFINITION_PROVIDER, useClass: AXPActivityInsightSettingProvider, multi: true },
1555
+ { provide: AXP_SEARCH_PROVIDER, useClass: AXMActivityInsightSearchCommandProvider, multi: true },
1556
+ { provide: AXP_ENTITY_ACTION_PLUGIN, useValue: activityTrackingPlugin, multi: true },
1557
+ { provide: AXP_ENTITY_MODIFIER, useValue: activityTrackingDefaultsModifier, multi: true },
1558
+ ...AXMActivityInsightEntityHookProviders.map((provider) => ({
1559
+ provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
1560
+ useValue: provider,
1561
+ multi: true,
1562
+ })),
1563
+ {
1564
+ provide: AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER,
1565
+ multi: true,
1566
+ useFactory: () => ({
1567
+ key: ACTIVITY_TRACK_HOOK_KEY,
1568
+ priority: 0,
1569
+ execute: (data) => {
1570
+ const tracker = inject(AXMActivityInsightTrackerService);
1571
+ if (data?.name) {
1572
+ void tracker.trackMany([data]);
1573
+ }
1574
+ },
1575
+ }),
1576
+ },
1577
+ provideAppStartUpTask(() => {
1578
+ const tracker = inject(AXMActivityInsightTrackerService);
1579
+ const pageView = inject(AXMActivityInsightPageViewInstrumentation);
1580
+ const command = inject(AXMActivityInsightCommandInstrumentation);
1581
+ const query = inject(AXMActivityInsightQueryInstrumentation);
1582
+ const entity = inject(AXMActivityInsightEntityInstrumentation);
1583
+ return {
1584
+ name: 'activity-insight-instrumentation',
1585
+ priority: AXP_APP_STARTUP_PRIORITY.activityInsight,
1586
+ run: async () => {
1587
+ tracker.init();
1588
+ pageView.init();
1589
+ command.init();
1590
+ query.init();
1591
+ entity.init();
1592
+ },
1593
+ };
1594
+ }),
1595
+ ],
1596
+ }]
1597
+ }] });
1598
+
1599
+ const AXM_ACTIVITY_TRACKING_RULE_PROVIDER = new InjectionToken('AXM_ACTIVITY_TRACKING_RULE_PROVIDER');
1600
+
1601
+ /**
1602
+ * Generated bundle index. Do not edit.
1603
+ */
1604
+
1605
+ export { AXMActivityInsightJourneyService, AXMActivityInsightModule, AXMActivityInsightTrackerService, AXM_ACTIVITY_TRACKING_RULE_PROVIDER };
1606
+ //# sourceMappingURL=acorex-platform-module-activity-insight-client.mjs.map