@equinor/fusion-framework-module-analytics 3.0.1 → 3.0.3
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.
- package/CHANGELOG.md +50 -0
- package/dist/esm/AnalyticsConfigurator.js +11 -2
- package/dist/esm/AnalyticsConfigurator.js.map +1 -1
- package/dist/esm/AnalyticsProvider.js +12 -2
- package/dist/esm/AnalyticsProvider.js.map +1 -1
- package/dist/esm/adapters/ConsoleAnalyticsAdapter.js +7 -1
- package/dist/esm/adapters/ConsoleAnalyticsAdapter.js.map +1 -1
- package/dist/esm/adapters/FusionAnalyticsAdapter.js +6 -1
- package/dist/esm/adapters/FusionAnalyticsAdapter.js.map +1 -1
- package/dist/esm/collectors/AppLoadedCollector.js +11 -3
- package/dist/esm/collectors/AppLoadedCollector.js.map +1 -1
- package/dist/esm/collectors/AppSelectedCollector.js +11 -2
- package/dist/esm/collectors/AppSelectedCollector.js.map +1 -1
- package/dist/esm/collectors/ContextSelectedCollector.js +11 -2
- package/dist/esm/collectors/ContextSelectedCollector.js.map +1 -1
- package/dist/esm/collectors/{BaseCollector.js → create-schema.js} +4 -1
- package/dist/esm/collectors/create-schema.js.map +1 -0
- package/dist/esm/collectors/utils/app-key-schema.js +14 -0
- package/dist/esm/collectors/utils/app-key-schema.js.map +1 -0
- package/dist/esm/collectors/utils/app-schema.js +19 -0
- package/dist/esm/collectors/utils/app-schema.js.map +1 -0
- package/dist/esm/collectors/utils/context-schema.js +19 -0
- package/dist/esm/collectors/utils/context-schema.js.map +1 -0
- package/dist/esm/collectors/utils/extract-app-key-metadata.js +12 -0
- package/dist/esm/collectors/utils/extract-app-key-metadata.js.map +1 -0
- package/dist/esm/collectors/utils/extract-app-metadata.js +17 -0
- package/dist/esm/collectors/utils/extract-app-metadata.js.map +1 -0
- package/dist/esm/collectors/utils/extract-context-metadata.js +16 -0
- package/dist/esm/collectors/utils/extract-context-metadata.js.map +1 -0
- package/dist/esm/enable-analytics.js +1 -0
- package/dist/esm/enable-analytics.js.map +1 -1
- package/dist/esm/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.js +9 -3
- package/dist/esm/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.js.map +1 -1
- package/dist/esm/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.js +5 -0
- package/dist/esm/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AnalyticsConfigurator.d.ts +3 -0
- package/dist/types/AnalyticsProvider.d.ts +3 -0
- package/dist/types/adapters/ConsoleAnalyticsAdapter.d.ts +7 -1
- package/dist/types/adapters/FusionAnalyticsAdapter.d.ts +6 -1
- package/dist/types/collectors/AppLoadedCollector.d.ts +9 -3
- package/dist/types/collectors/AppSelectedCollector.d.ts +8 -2
- package/dist/types/collectors/ContextSelectedCollector.d.ts +8 -2
- package/dist/types/collectors/{BaseCollector.d.ts → create-schema.d.ts} +2 -0
- package/dist/types/collectors/utils/app-key-schema.d.ts +12 -0
- package/dist/types/collectors/utils/app-schema.d.ts +18 -0
- package/dist/types/collectors/utils/{extractContextMetadata.d.ts → context-schema.d.ts} +0 -8
- package/dist/types/collectors/utils/extract-app-key-metadata.d.ts +10 -0
- package/dist/types/collectors/utils/extract-app-metadata.d.ts +10 -0
- package/dist/types/collectors/utils/extract-context-metadata.d.ts +10 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +14 -14
- package/src/AnalyticsConfigurator.ts +7 -0
- package/src/AnalyticsProvider.ts +12 -2
- package/src/adapters/ConsoleAnalyticsAdapter.ts +7 -1
- package/src/adapters/FusionAnalyticsAdapter.ts +6 -1
- package/src/collectors/AppLoadedCollector.ts +11 -7
- package/src/collectors/AppSelectedCollector.ts +11 -6
- package/src/collectors/ContextSelectedCollector.ts +11 -6
- package/src/collectors/{BaseCollector.ts → create-schema.ts} +3 -0
- package/src/collectors/utils/app-key-schema.ts +17 -0
- package/src/collectors/utils/app-schema.ts +22 -0
- package/src/collectors/utils/{extractContextMetadata.ts → context-schema.ts} +0 -17
- package/src/collectors/utils/extract-app-key-metadata.ts +15 -0
- package/src/collectors/utils/extract-app-metadata.ts +20 -0
- package/src/collectors/utils/extract-context-metadata.ts +19 -0
- package/src/enable-analytics.ts +1 -0
- package/src/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.ts +9 -1
- package/src/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.ts +5 -0
- package/src/version.ts +1 -1
- package/dist/esm/collectors/BaseCollector.js.map +0 -1
- package/dist/esm/collectors/utils/extractAppMetadata.js +0 -58
- package/dist/esm/collectors/utils/extractAppMetadata.js.map +0 -1
- package/dist/esm/collectors/utils/extractContextMetadata.js +0 -34
- package/dist/esm/collectors/utils/extractContextMetadata.js.map +0 -1
- package/dist/types/collectors/utils/extractAppMetadata.d.ts +0 -44
- package/src/collectors/utils/extractAppMetadata.ts +0 -68
|
@@ -22,6 +22,9 @@ import type { AnalyticsEvent } from './types.js';
|
|
|
22
22
|
*/
|
|
23
23
|
export declare class AnalyticsConfigurator extends BaseConfigBuilder<AnalyticsConfig> implements IAnalyticsConfigurator {
|
|
24
24
|
#private;
|
|
25
|
+
/**
|
|
26
|
+
* Registers the async resolvers for `collectors` and `adapters` config keys.
|
|
27
|
+
*/
|
|
25
28
|
constructor();
|
|
26
29
|
/**
|
|
27
30
|
* Registers an analytics collector factory.
|
|
@@ -23,6 +23,9 @@ import { type ObservableInput, Subscription } from 'rxjs';
|
|
|
23
23
|
*/
|
|
24
24
|
export declare class AnalyticsProvider extends BaseModuleProvider<AnalyticsConfig> implements IAnalyticsProvider {
|
|
25
25
|
#private;
|
|
26
|
+
/**
|
|
27
|
+
* @param config - Resolved analytics module configuration, including collectors and adapters.
|
|
28
|
+
*/
|
|
26
29
|
constructor(config: AnalyticsConfig);
|
|
27
30
|
/**
|
|
28
31
|
* Initialises all adapters and collectors, then wires collector output into
|
|
@@ -17,7 +17,13 @@ import type { AnalyticsEvent } from '../types.js';
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
export declare class ConsoleAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent> implements IAnalyticsAdapter {
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Logs the event to the console.
|
|
22
|
+
*
|
|
23
|
+
* @param event - The analytics event to log.
|
|
24
|
+
* @returns Resolves once the event has been logged.
|
|
25
|
+
*/
|
|
21
26
|
registerAnalytic(event: T): Promise<void> | void;
|
|
27
|
+
/** No teardown is required for this adapter. */
|
|
22
28
|
[Symbol.dispose](): void;
|
|
23
29
|
}
|
|
@@ -41,7 +41,12 @@ export declare class FusionAnalyticsAdapter<T extends AnalyticsEvent = Analytics
|
|
|
41
41
|
portalId: string;
|
|
42
42
|
logExporter: OTLPExporterBase<ReadableLogRecord[]>;
|
|
43
43
|
});
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Maps an analytics event to an OTLP `LogRecord` and emits it via the logger.
|
|
46
|
+
*
|
|
47
|
+
* @param event - The analytics event to map and emit.
|
|
48
|
+
* @returns Resolves once the event has been emitted to the logger.
|
|
49
|
+
*/
|
|
45
50
|
registerAnalytic(event: T): Promise<void> | void;
|
|
46
51
|
/** Shuts down the log exporter and logger provider, flushing remaining records. */
|
|
47
52
|
[Symbol.dispose](): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BaseCollector } from './
|
|
2
|
-
import { type AppItemType } from './utils/
|
|
3
|
-
import { type ContextItemType } from './utils/
|
|
1
|
+
import { BaseCollector } from './create-schema.js';
|
|
2
|
+
import { type AppItemType } from './utils/app-schema.js';
|
|
3
|
+
import { type ContextItemType } from './utils/context-schema.js';
|
|
4
4
|
import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
5
5
|
import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
|
|
6
6
|
import type { IEventModuleProvider } from '@equinor/fusion-framework-module-event';
|
|
@@ -32,6 +32,12 @@ export declare class AppLoadedCollector extends BaseCollector<AppItemType, {
|
|
|
32
32
|
* @param appProvider - Fusion app module provider for fallback manifest data.
|
|
33
33
|
*/
|
|
34
34
|
constructor(eventProvider: IEventModuleProvider, appProvider: AppModuleProvider);
|
|
35
|
+
/**
|
|
36
|
+
* Builds an observable that emits an analytics event whenever the app-loaded
|
|
37
|
+
* event fires, falling back to `appProvider` for manifest data when needed.
|
|
38
|
+
*
|
|
39
|
+
* @returns An observable input emitting the app-loaded event's value and attributes.
|
|
40
|
+
*/
|
|
35
41
|
_initialize(): ObservableInput<{
|
|
36
42
|
value: AppItemType;
|
|
37
43
|
attributes: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ObservableInput } from 'rxjs';
|
|
2
2
|
import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
3
|
-
import { BaseCollector } from './
|
|
4
|
-
import { type AppKeyType } from './utils/
|
|
3
|
+
import { BaseCollector } from './create-schema.js';
|
|
4
|
+
import { type AppKeyType } from './utils/app-key-schema.js';
|
|
5
5
|
import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
|
|
6
6
|
/**
|
|
7
7
|
* Collector that emits an analytics event whenever the active Fusion application changes.
|
|
@@ -26,6 +26,12 @@ export declare class AppSelectedCollector extends BaseCollector<AppKeyType, {
|
|
|
26
26
|
* @param appProvider - Fusion app module provider to observe.
|
|
27
27
|
*/
|
|
28
28
|
constructor(appProvider: AppModuleProvider);
|
|
29
|
+
/**
|
|
30
|
+
* Builds an observable that emits an analytics event each time the selected
|
|
31
|
+
* app changes, including the previous app's key metadata.
|
|
32
|
+
*
|
|
33
|
+
* @returns An observable input emitting the newly selected app's value and attributes.
|
|
34
|
+
*/
|
|
29
35
|
_initialize(): ObservableInput<{
|
|
30
36
|
value: AppKeyType;
|
|
31
37
|
attributes: {
|
|
@@ -2,8 +2,8 @@ import { type ObservableInput } from 'rxjs';
|
|
|
2
2
|
import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
3
3
|
import type { IContextProvider } from '@equinor/fusion-framework-module-context';
|
|
4
4
|
import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
|
|
5
|
-
import { BaseCollector } from './
|
|
6
|
-
import { type ContextItemType } from './utils/
|
|
5
|
+
import { BaseCollector } from './create-schema.js';
|
|
6
|
+
import { type ContextItemType } from './utils/context-schema.js';
|
|
7
7
|
/**
|
|
8
8
|
* Collector that emits an analytics event whenever the active Fusion context changes.
|
|
9
9
|
*
|
|
@@ -31,6 +31,12 @@ export declare class ContextSelectedCollector extends BaseCollector<ContextItemT
|
|
|
31
31
|
* @param appProvider - Fusion app module provider for the current app key.
|
|
32
32
|
*/
|
|
33
33
|
constructor(contextProvider: IContextProvider, appProvider: AppModuleProvider);
|
|
34
|
+
/**
|
|
35
|
+
* Builds an observable that emits an analytics event each time the selected
|
|
36
|
+
* context changes, including the previous context and current app key.
|
|
37
|
+
*
|
|
38
|
+
* @returns An observable input emitting the newly selected context's value and attributes.
|
|
39
|
+
*/
|
|
34
40
|
_initialize(): ObservableInput<{
|
|
35
41
|
value: ContextItemType;
|
|
36
42
|
attributes: {
|
|
@@ -70,6 +70,8 @@ export declare abstract class BaseCollector<TValue extends AnyValue, TAttr exten
|
|
|
70
70
|
* Subscribes to the source returned by {@link BaseCollector._initialize},
|
|
71
71
|
* validates each emission with the Zod schema, and publishes through the
|
|
72
72
|
* internal subject.
|
|
73
|
+
*
|
|
74
|
+
* @returns Resolves once the source has been subscribed to.
|
|
73
75
|
*/
|
|
74
76
|
initialize(): Promise<void> | void;
|
|
75
77
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for an object containing an optional `appKey` string.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Used by {@link AppSelectedCollector} to validate the event body.
|
|
7
|
+
*/
|
|
8
|
+
export declare const appKeySchema: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
9
|
+
appKey: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>>>;
|
|
11
|
+
/** Inferred type from {@link appKeySchema}. */
|
|
12
|
+
export type AppKeyType = z.infer<typeof appKeySchema>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for a Fusion application metadata object.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Validates core app fields (appKey, displayName, type) and optional build
|
|
7
|
+
* and category information. Used by {@link AppLoadedCollector}.
|
|
8
|
+
*/
|
|
9
|
+
export declare const appSchema: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
appKey: z.ZodString;
|
|
11
|
+
displayName: z.ZodString;
|
|
12
|
+
type: z.ZodString;
|
|
13
|
+
categoryName: z.ZodOptional<z.ZodString>;
|
|
14
|
+
buildVersion: z.ZodOptional<z.ZodString>;
|
|
15
|
+
buildTag: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
16
|
+
}, z.core.$strip>>;
|
|
17
|
+
/** Inferred type from {@link appSchema}. */
|
|
18
|
+
export type AppItemType = z.infer<typeof appSchema>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type { ContextItem } from '@equinor/fusion-framework-module-context';
|
|
3
2
|
/**
|
|
4
3
|
* Zod schema for a Fusion context metadata object.
|
|
5
4
|
*
|
|
@@ -16,10 +15,3 @@ export declare const contextSchema: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
|
16
15
|
}, z.core.$strip>>>;
|
|
17
16
|
/** Inferred type from {@link contextSchema}. */
|
|
18
17
|
export type ContextItemType = z.infer<typeof contextSchema>;
|
|
19
|
-
/**
|
|
20
|
-
* Extracts context metadata from a `ContextItem` for analytics events.
|
|
21
|
-
*
|
|
22
|
-
* @param context - The Fusion context item.
|
|
23
|
-
* @returns An object with id, type, and optional title, externalId, and source.
|
|
24
|
-
*/
|
|
25
|
-
export declare const extractContextMetadata: (context: ContextItem) => z.input<typeof contextSchema>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CurrentApp } from '@equinor/fusion-framework-module-app';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { appKeySchema } from './app-key-schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extracts app-key metadata from a `CurrentApp` instance.
|
|
6
|
+
*
|
|
7
|
+
* @param app - The current app object.
|
|
8
|
+
* @returns An object containing the optional `appKey`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const extractAppKeyMetadata: (app: CurrentApp) => z.input<typeof appKeySchema>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AppManifest } from '@equinor/fusion-framework-module-app';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { appSchema } from './app-schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extracts detailed app metadata from an `AppManifest` for analytics events.
|
|
6
|
+
*
|
|
7
|
+
* @param app - The application manifest.
|
|
8
|
+
* @returns An object with appKey, displayName, type, and optional build/category info.
|
|
9
|
+
*/
|
|
10
|
+
export declare const extractAppMetadata: (app: AppManifest) => z.input<typeof appSchema>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContextItem } from '@equinor/fusion-framework-module-context';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { contextSchema } from './context-schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extracts context metadata from a `ContextItem` for analytics events.
|
|
6
|
+
*
|
|
7
|
+
* @param context - The Fusion context item.
|
|
8
|
+
* @returns An object with id, type, and optional title, externalId, and source.
|
|
9
|
+
*/
|
|
10
|
+
export declare const extractContextMetadata: (context: ContextItem) => z.input<typeof contextSchema>;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "3.0.
|
|
1
|
+
export declare const version = "3.0.3";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-analytics",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Fusion module for collecting and exporting application analytics using OpenTelemetry standards",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,29 +39,29 @@
|
|
|
39
39
|
"directory": "packages/modules/analytics"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@opentelemetry/api-logs": "^0.
|
|
43
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
44
|
-
"@opentelemetry/otlp-exporter-base": "^0.
|
|
45
|
-
"@opentelemetry/otlp-transformer": "^0.
|
|
42
|
+
"@opentelemetry/api-logs": "^0.221.0",
|
|
43
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
|
|
44
|
+
"@opentelemetry/otlp-exporter-base": "^0.221.0",
|
|
45
|
+
"@opentelemetry/otlp-transformer": "^0.221.0",
|
|
46
46
|
"@opentelemetry/resources": "^2.2.0",
|
|
47
|
-
"@opentelemetry/sdk-logs": "^0.
|
|
47
|
+
"@opentelemetry/sdk-logs": "^0.221.0",
|
|
48
48
|
"deepmerge": "^4.3.1",
|
|
49
49
|
"rxjs": "^7.8.1",
|
|
50
50
|
"uuid": "^14.0.0",
|
|
51
51
|
"zod": "^4.4.3",
|
|
52
|
-
"@equinor/fusion-framework-module": "
|
|
53
|
-
"@equinor/fusion-framework-module-app": "8.0.
|
|
54
|
-
"@equinor/fusion-framework-module
|
|
55
|
-
"@equinor/fusion-framework-module-event": "6.0.
|
|
56
|
-
"@equinor/fusion-framework-module-http": "8.0.
|
|
52
|
+
"@equinor/fusion-framework-module-context": "8.0.1",
|
|
53
|
+
"@equinor/fusion-framework-module-app": "8.0.3",
|
|
54
|
+
"@equinor/fusion-framework-module": "6.1.1",
|
|
55
|
+
"@equinor/fusion-framework-module-event": "6.0.1",
|
|
56
|
+
"@equinor/fusion-framework-module-http": "8.0.4"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"typescript": "^
|
|
59
|
+
"typescript": "^7.0.2",
|
|
60
60
|
"vitest": "^4.1.0",
|
|
61
|
-
"@equinor/fusion-observable": "^9.1.
|
|
61
|
+
"@equinor/fusion-observable": "^9.1.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@equinor/fusion-observable": "9.1.
|
|
64
|
+
"@equinor/fusion-observable": "9.1.1"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsc -b",
|
|
@@ -35,6 +35,9 @@ export class AnalyticsConfigurator
|
|
|
35
35
|
#collectorCallbacks: Record<string, ConfigBuilderCallback<IAnalyticsCollector>> = {};
|
|
36
36
|
#adapterCallbacks: Record<string, ConfigBuilderCallback<IAnalyticsAdapter>> = {};
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Registers the async resolvers for `collectors` and `adapters` config keys.
|
|
40
|
+
*/
|
|
38
41
|
constructor() {
|
|
39
42
|
super();
|
|
40
43
|
|
|
@@ -42,8 +45,10 @@ export class AnalyticsConfigurator
|
|
|
42
45
|
this._set(
|
|
43
46
|
'collectors',
|
|
44
47
|
(args: ConfigBuilderCallbackArgs): ObservableInput<Record<string, IAnalyticsCollector>> => {
|
|
48
|
+
// Resolve each collector factory and drop any that resolve to a falsy value
|
|
45
49
|
return from(Object.entries(this.#collectorCallbacks)).pipe(
|
|
46
50
|
mergeMap(([identifier, collectorFn]) =>
|
|
51
|
+
// Resolve the collector's own Promise/Observable factory result
|
|
47
52
|
from(collectorFn(args)).pipe(
|
|
48
53
|
filter((collector): collector is IAnalyticsCollector => !!collector),
|
|
49
54
|
map((collector) => [identifier, collector] as const),
|
|
@@ -66,8 +71,10 @@ export class AnalyticsConfigurator
|
|
|
66
71
|
this._set(
|
|
67
72
|
'adapters',
|
|
68
73
|
(args: ConfigBuilderCallbackArgs): ObservableInput<Record<string, IAnalyticsAdapter>> => {
|
|
74
|
+
// Resolve each adapter factory and drop any that resolve to a falsy value
|
|
69
75
|
return from(Object.entries(this.#adapterCallbacks)).pipe(
|
|
70
76
|
mergeMap(([identifier, adapterFn]) =>
|
|
77
|
+
// Resolve the adapter's own Promise/Observable factory result
|
|
71
78
|
from(adapterFn(args)).pipe(
|
|
72
79
|
filter((adapter): adapter is IAnalyticsAdapter => !!adapter),
|
|
73
80
|
map((adapter) => [identifier, adapter] as const),
|
package/src/AnalyticsProvider.ts
CHANGED
|
@@ -17,6 +17,9 @@ import type { IAnalyticsAdapter } from './adapters/AnalyticsAdapter.interface.js
|
|
|
17
17
|
* with either `subscription.unsubscribe()` or `using` / `Symbol.dispose`.
|
|
18
18
|
*/
|
|
19
19
|
class DisposableSubscription extends Subscription {
|
|
20
|
+
/**
|
|
21
|
+
* @param subscription - The underlying subscription to wrap with `Symbol.dispose` support.
|
|
22
|
+
*/
|
|
20
23
|
constructor(subscription: Subscription) {
|
|
21
24
|
super(subscription.unsubscribe);
|
|
22
25
|
}
|
|
@@ -52,6 +55,9 @@ export class AnalyticsProvider
|
|
|
52
55
|
#collectors: Record<string, IAnalyticsCollector>;
|
|
53
56
|
#adapters: Record<string, IAnalyticsAdapter>;
|
|
54
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @param config - Resolved analytics module configuration, including collectors and adapters.
|
|
60
|
+
*/
|
|
55
61
|
constructor(config: AnalyticsConfig) {
|
|
56
62
|
super({ version, config });
|
|
57
63
|
|
|
@@ -78,9 +84,11 @@ export class AnalyticsProvider
|
|
|
78
84
|
* @returns A promise that resolves when all adapters and collectors are initialised.
|
|
79
85
|
*/
|
|
80
86
|
async initialize(): Promise<void> {
|
|
87
|
+
// Kick off initialization for every collector, tolerating individual failures
|
|
81
88
|
const initializedCollectors = Object.values(this.#collectors).map((collector) =>
|
|
82
89
|
Promise.resolve(collector.initialize?.()),
|
|
83
90
|
);
|
|
91
|
+
// Kick off initialization for every adapter, tolerating individual failures
|
|
84
92
|
const initializedAdapters = Object.values(this.#adapters).map((adapters) =>
|
|
85
93
|
Promise.resolve(adapters.initialize?.()),
|
|
86
94
|
);
|
|
@@ -88,6 +96,7 @@ export class AnalyticsProvider
|
|
|
88
96
|
await Promise.allSettled(initializedCollectors);
|
|
89
97
|
await Promise.allSettled(initializedAdapters);
|
|
90
98
|
|
|
99
|
+
// Forward every collector's emissions into the shared analytics subject
|
|
91
100
|
for (const collector of Object.values(this.#collectors)) {
|
|
92
101
|
const subscription = collector.subscribe({
|
|
93
102
|
next: (event) => {
|
|
@@ -100,6 +109,7 @@ export class AnalyticsProvider
|
|
|
100
109
|
|
|
101
110
|
const adapterSubscription = this.#analytics.subscribe({
|
|
102
111
|
next: (event) => {
|
|
112
|
+
// Dispatch every emitted event to all registered adapters
|
|
103
113
|
for (const adapter of Object.values(this.#adapters)) {
|
|
104
114
|
adapter.registerAnalytic(event);
|
|
105
115
|
}
|
|
@@ -114,7 +124,7 @@ export class AnalyticsProvider
|
|
|
114
124
|
* @param event - The analytics event to track.
|
|
115
125
|
*/
|
|
116
126
|
trackAnalytic(event: AnalyticsEvent): void {
|
|
117
|
-
//
|
|
127
|
+
// TODO(#5098): Validate AnalyticsEvent includes name, value and attributes
|
|
118
128
|
this.#analytics.next(event);
|
|
119
129
|
}
|
|
120
130
|
|
|
@@ -127,7 +137,7 @@ export class AnalyticsProvider
|
|
|
127
137
|
*/
|
|
128
138
|
trackAnalytic$(analytic$: ObservableInput<AnalyticsEvent>): Disposable & Subscription {
|
|
129
139
|
const subscription = from(analytic$)
|
|
130
|
-
//
|
|
140
|
+
// TODO(#5098): Validate AnalyticsEvent includes name, value and attributes
|
|
131
141
|
.subscribe({
|
|
132
142
|
next: (event) => {
|
|
133
143
|
this.#analytics.next(event);
|
|
@@ -20,11 +20,17 @@ import type { AnalyticsEvent } from '../types.js';
|
|
|
20
20
|
export class ConsoleAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
|
|
21
21
|
implements IAnalyticsAdapter
|
|
22
22
|
{
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Logs the event to the console.
|
|
25
|
+
*
|
|
26
|
+
* @param event - The analytics event to log.
|
|
27
|
+
* @returns Resolves once the event has been logged.
|
|
28
|
+
*/
|
|
24
29
|
registerAnalytic(event: T): Promise<void> | void {
|
|
25
30
|
console.log('Analytics::Adapter::Console', event);
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
/** No teardown is required for this adapter. */
|
|
28
34
|
[Symbol.dispose]() {
|
|
29
35
|
// no-op
|
|
30
36
|
}
|
|
@@ -71,7 +71,12 @@ export class FusionAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
|
|
|
71
71
|
this.#logger = this.#loggerProvider.getLogger('fusion');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Maps an analytics event to an OTLP `LogRecord` and emits it via the logger.
|
|
76
|
+
*
|
|
77
|
+
* @param event - The analytics event to map and emit.
|
|
78
|
+
* @returns Resolves once the event has been emitted to the logger.
|
|
79
|
+
*/
|
|
75
80
|
registerAnalytic(event: T): Promise<void> | void {
|
|
76
81
|
const logRecord: Partial<LogRecord> = {
|
|
77
82
|
eventName: event.name,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { BaseCollector, createSchema } from './
|
|
2
|
-
import { type AppItemType, appSchema
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
extractContextMetadata,
|
|
7
|
-
} from './utils/extractContextMetadata.js';
|
|
1
|
+
import { BaseCollector, createSchema } from './create-schema.js';
|
|
2
|
+
import { type AppItemType, appSchema } from './utils/app-schema.js';
|
|
3
|
+
import { extractAppMetadata } from './utils/extract-app-metadata.js';
|
|
4
|
+
import { type ContextItemType, contextSchema } from './utils/context-schema.js';
|
|
5
|
+
import { extractContextMetadata } from './utils/extract-context-metadata.js';
|
|
8
6
|
import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
9
7
|
|
|
10
8
|
import type {
|
|
@@ -58,6 +56,12 @@ export class AppLoadedCollector
|
|
|
58
56
|
this.#appProvider = appProvider;
|
|
59
57
|
}
|
|
60
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Builds an observable that emits an analytics event whenever the app-loaded
|
|
61
|
+
* event fires, falling back to `appProvider` for manifest data when needed.
|
|
62
|
+
*
|
|
63
|
+
* @returns An observable input emitting the app-loaded event's value and attributes.
|
|
64
|
+
*/
|
|
61
65
|
_initialize(): ObservableInput<{
|
|
62
66
|
value: AppItemType;
|
|
63
67
|
attributes: { context?: ContextItemType };
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { map, type ObservableInput, pairwise } from 'rxjs';
|
|
2
2
|
import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
3
|
-
import { BaseCollector, createSchema } from './
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
appKeySchema,
|
|
7
|
-
extractAppKeyMetadata,
|
|
8
|
-
} from './utils/extractAppMetadata.js';
|
|
3
|
+
import { BaseCollector, createSchema } from './create-schema.js';
|
|
4
|
+
import { type AppKeyType, appKeySchema } from './utils/app-key-schema.js';
|
|
5
|
+
import { extractAppKeyMetadata } from './utils/extract-app-key-metadata.js';
|
|
9
6
|
|
|
10
7
|
import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
|
|
11
8
|
|
|
@@ -43,12 +40,20 @@ export class AppSelectedCollector
|
|
|
43
40
|
this.#appProvider = appProvider;
|
|
44
41
|
}
|
|
45
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Builds an observable that emits an analytics event each time the selected
|
|
45
|
+
* app changes, including the previous app's key metadata.
|
|
46
|
+
*
|
|
47
|
+
* @returns An observable input emitting the newly selected app's value and attributes.
|
|
48
|
+
*/
|
|
46
49
|
_initialize(): ObservableInput<{
|
|
47
50
|
value: AppKeyType;
|
|
48
51
|
attributes: { previous?: AppKeyType };
|
|
49
52
|
}> {
|
|
53
|
+
// Pair each app-selection emission with the previously selected app
|
|
50
54
|
const appSelected$ = this.#appProvider.current$.pipe(pairwise());
|
|
51
55
|
|
|
56
|
+
// Map the [previous, next] pair into the analytics event shape
|
|
52
57
|
const data$ = appSelected$.pipe(
|
|
53
58
|
map(([prev, next]) => {
|
|
54
59
|
return {
|
|
@@ -3,12 +3,9 @@ import type { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
|
|
|
3
3
|
import type { IContextProvider } from '@equinor/fusion-framework-module-context';
|
|
4
4
|
import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { BaseCollector, createSchema } from './
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
contextSchema,
|
|
10
|
-
extractContextMetadata,
|
|
11
|
-
} from './utils/extractContextMetadata.js';
|
|
6
|
+
import { BaseCollector, createSchema } from './create-schema.js';
|
|
7
|
+
import { type ContextItemType, contextSchema } from './utils/context-schema.js';
|
|
8
|
+
import { extractContextMetadata } from './utils/extract-context-metadata.js';
|
|
12
9
|
|
|
13
10
|
/** Zod schema for the `context-selected` event (value + attributes). */
|
|
14
11
|
const eventSchema = createSchema(
|
|
@@ -50,10 +47,17 @@ export class ContextSelectedCollector
|
|
|
50
47
|
this.#appProvider = appProvider;
|
|
51
48
|
}
|
|
52
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Builds an observable that emits an analytics event each time the selected
|
|
52
|
+
* context changes, including the previous context and current app key.
|
|
53
|
+
*
|
|
54
|
+
* @returns An observable input emitting the newly selected context's value and attributes.
|
|
55
|
+
*/
|
|
53
56
|
_initialize(): ObservableInput<{
|
|
54
57
|
value: ContextItemType;
|
|
55
58
|
attributes: { previous?: ContextItemType; appKey?: string };
|
|
56
59
|
}> {
|
|
60
|
+
// Track context changes, pairing each new context with the previously selected one
|
|
57
61
|
const contextSelected$ = this.#contextProvider.currentContext$.pipe(
|
|
58
62
|
// Only emit when an actual change has happened.
|
|
59
63
|
distinctUntilChanged((prev, curr) => prev?.id === curr?.id),
|
|
@@ -61,6 +65,7 @@ export class ContextSelectedCollector
|
|
|
61
65
|
pairwise(),
|
|
62
66
|
);
|
|
63
67
|
|
|
68
|
+
// Map the [previous, next] pair into the analytics event shape
|
|
64
69
|
const data$ = contextSelected$.pipe(
|
|
65
70
|
map(([prev, next]) => {
|
|
66
71
|
return {
|
|
@@ -92,8 +92,11 @@ export abstract class BaseCollector<
|
|
|
92
92
|
* Subscribes to the source returned by {@link BaseCollector._initialize},
|
|
93
93
|
* validates each emission with the Zod schema, and publishes through the
|
|
94
94
|
* internal subject.
|
|
95
|
+
*
|
|
96
|
+
* @returns Resolves once the source has been subscribed to.
|
|
95
97
|
*/
|
|
96
98
|
initialize(): Promise<void> | void {
|
|
99
|
+
// Shape each emission into the named/validated event before publishing
|
|
97
100
|
from(this._initialize())
|
|
98
101
|
.pipe(
|
|
99
102
|
map(({ value, attributes }) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for an object containing an optional `appKey` string.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Used by {@link AppSelectedCollector} to validate the event body.
|
|
8
|
+
*/
|
|
9
|
+
export const appKeySchema = z
|
|
10
|
+
.object({
|
|
11
|
+
appKey: z.string().optional(),
|
|
12
|
+
})
|
|
13
|
+
.optional()
|
|
14
|
+
.nullable();
|
|
15
|
+
|
|
16
|
+
/** Inferred type from {@link appKeySchema}. */
|
|
17
|
+
export type AppKeyType = z.infer<typeof appKeySchema>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for a Fusion application metadata object.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Validates core app fields (appKey, displayName, type) and optional build
|
|
8
|
+
* and category information. Used by {@link AppLoadedCollector}.
|
|
9
|
+
*/
|
|
10
|
+
export const appSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
appKey: z.string(),
|
|
13
|
+
displayName: z.string(),
|
|
14
|
+
type: z.string(),
|
|
15
|
+
categoryName: z.string().optional(),
|
|
16
|
+
buildVersion: z.string().optional(),
|
|
17
|
+
buildTag: z.string().optional().nullable(),
|
|
18
|
+
})
|
|
19
|
+
.optional();
|
|
20
|
+
|
|
21
|
+
/** Inferred type from {@link appSchema}. */
|
|
22
|
+
export type AppItemType = z.infer<typeof appSchema>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type { ContextItem } from '@equinor/fusion-framework-module-context';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Zod schema for a Fusion context metadata object.
|
|
@@ -21,19 +20,3 @@ export const contextSchema = z
|
|
|
21
20
|
|
|
22
21
|
/** Inferred type from {@link contextSchema}. */
|
|
23
22
|
export type ContextItemType = z.infer<typeof contextSchema>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Extracts context metadata from a `ContextItem` for analytics events.
|
|
27
|
-
*
|
|
28
|
-
* @param context - The Fusion context item.
|
|
29
|
-
* @returns An object with id, type, and optional title, externalId, and source.
|
|
30
|
-
*/
|
|
31
|
-
export const extractContextMetadata = (context: ContextItem): z.input<typeof contextSchema> => {
|
|
32
|
-
return {
|
|
33
|
-
id: context.id,
|
|
34
|
-
externalId: context.externalId ?? undefined,
|
|
35
|
-
title: context.title ?? undefined,
|
|
36
|
-
type: context.type.id,
|
|
37
|
-
source: context.source ?? undefined,
|
|
38
|
-
};
|
|
39
|
-
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CurrentApp } from '@equinor/fusion-framework-module-app';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { appKeySchema } from './app-key-schema.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts app-key metadata from a `CurrentApp` instance.
|
|
7
|
+
*
|
|
8
|
+
* @param app - The current app object.
|
|
9
|
+
* @returns An object containing the optional `appKey`.
|
|
10
|
+
*/
|
|
11
|
+
export const extractAppKeyMetadata = (app: CurrentApp): z.input<typeof appKeySchema> => {
|
|
12
|
+
return {
|
|
13
|
+
appKey: app?.appKey,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AppManifest } from '@equinor/fusion-framework-module-app';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import type { appSchema } from './app-schema.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts detailed app metadata from an `AppManifest` for analytics events.
|
|
7
|
+
*
|
|
8
|
+
* @param app - The application manifest.
|
|
9
|
+
* @returns An object with appKey, displayName, type, and optional build/category info.
|
|
10
|
+
*/
|
|
11
|
+
export const extractAppMetadata = (app: AppManifest): z.input<typeof appSchema> => {
|
|
12
|
+
return {
|
|
13
|
+
appKey: app.appKey,
|
|
14
|
+
displayName: app.displayName,
|
|
15
|
+
type: app.type,
|
|
16
|
+
categoryName: app.category?.name,
|
|
17
|
+
buildVersion: app.build?.version,
|
|
18
|
+
buildTag: app.build?.tag,
|
|
19
|
+
};
|
|
20
|
+
};
|