@equinor/fusion-framework-module-analytics 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +147 -130
  3. package/dist/esm/AnalyticsConfigurator.js +22 -17
  4. package/dist/esm/AnalyticsConfigurator.js.map +1 -1
  5. package/dist/esm/AnalyticsProvider.js +39 -18
  6. package/dist/esm/AnalyticsProvider.js.map +1 -1
  7. package/dist/esm/adapters/ConsoleAnalyticsAdapter.js +15 -1
  8. package/dist/esm/adapters/ConsoleAnalyticsAdapter.js.map +1 -1
  9. package/dist/esm/adapters/FusionAnalyticsAdapter.js +32 -3
  10. package/dist/esm/adapters/FusionAnalyticsAdapter.js.map +1 -1
  11. package/dist/esm/adapters/index.js.map +1 -1
  12. package/dist/esm/collectors/AppLoadedCollector.js +21 -5
  13. package/dist/esm/collectors/AppLoadedCollector.js.map +1 -1
  14. package/dist/esm/collectors/AppSelectedCollector.js +17 -5
  15. package/dist/esm/collectors/AppSelectedCollector.js.map +1 -1
  16. package/dist/esm/collectors/BaseCollector.js +48 -3
  17. package/dist/esm/collectors/BaseCollector.js.map +1 -1
  18. package/dist/esm/collectors/ContextSelectedCollector.js +20 -5
  19. package/dist/esm/collectors/ContextSelectedCollector.js.map +1 -1
  20. package/dist/esm/collectors/index.js.map +1 -1
  21. package/dist/esm/collectors/utils/extractAppMetadata.js +25 -4
  22. package/dist/esm/collectors/utils/extractAppMetadata.js.map +1 -1
  23. package/dist/esm/collectors/utils/extractContextMetadata.js +13 -2
  24. package/dist/esm/collectors/utils/extractContextMetadata.js.map +1 -1
  25. package/dist/esm/enable-analytics.js +18 -3
  26. package/dist/esm/enable-analytics.js.map +1 -1
  27. package/dist/esm/index.js +18 -0
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.js +26 -4
  30. package/dist/esm/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.js.map +1 -1
  31. package/dist/esm/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.js +36 -7
  32. package/dist/esm/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.js.map +1 -1
  33. package/dist/esm/logExporters/index.js +8 -0
  34. package/dist/esm/logExporters/index.js.map +1 -1
  35. package/dist/esm/module.js +8 -8
  36. package/dist/esm/version.js +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/dist/types/AnalyticsConfigurator.d.ts +22 -17
  39. package/dist/types/AnalyticsConfigurator.interface.d.ts +41 -12
  40. package/dist/types/AnalyticsProvider.d.ts +32 -18
  41. package/dist/types/AnalyticsProvider.interface.d.ts +28 -6
  42. package/dist/types/adapters/AnalyticsAdapter.interface.d.ts +15 -2
  43. package/dist/types/adapters/ConsoleAnalyticsAdapter.d.ts +15 -1
  44. package/dist/types/adapters/FusionAnalyticsAdapter.d.ts +32 -3
  45. package/dist/types/adapters/index.d.ts +8 -0
  46. package/dist/types/collectors/AnalyticsCollector.interface.d.ts +11 -2
  47. package/dist/types/collectors/AppLoadedCollector.d.ts +20 -2
  48. package/dist/types/collectors/AppSelectedCollector.d.ts +16 -2
  49. package/dist/types/collectors/BaseCollector.d.ts +58 -3
  50. package/dist/types/collectors/ContextSelectedCollector.d.ts +19 -2
  51. package/dist/types/collectors/index.d.ts +8 -0
  52. package/dist/types/collectors/utils/extractAppMetadata.d.ts +27 -0
  53. package/dist/types/collectors/utils/extractContextMetadata.d.ts +14 -0
  54. package/dist/types/enable-analytics.d.ts +19 -3
  55. package/dist/types/index.d.ts +18 -0
  56. package/dist/types/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.d.ts +21 -3
  57. package/dist/types/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.d.ts +24 -1
  58. package/dist/types/logExporters/index.d.ts +8 -0
  59. package/dist/types/module.d.ts +15 -15
  60. package/dist/types/types.d.ts +38 -7
  61. package/dist/types/version.d.ts +1 -1
  62. package/package.json +11 -11
  63. package/src/AnalyticsConfigurator.interface.ts +41 -12
  64. package/src/AnalyticsConfigurator.ts +22 -17
  65. package/src/AnalyticsProvider.interface.ts +28 -6
  66. package/src/AnalyticsProvider.ts +39 -18
  67. package/src/adapters/AnalyticsAdapter.interface.ts +15 -2
  68. package/src/adapters/ConsoleAnalyticsAdapter.ts +15 -1
  69. package/src/adapters/FusionAnalyticsAdapter.ts +32 -3
  70. package/src/adapters/index.ts +8 -0
  71. package/src/collectors/AnalyticsCollector.interface.ts +11 -2
  72. package/src/collectors/AppLoadedCollector.ts +21 -5
  73. package/src/collectors/AppSelectedCollector.ts +17 -5
  74. package/src/collectors/BaseCollector.ts +58 -3
  75. package/src/collectors/ContextSelectedCollector.ts +20 -5
  76. package/src/collectors/index.ts +8 -0
  77. package/src/collectors/utils/extractAppMetadata.ts +27 -4
  78. package/src/collectors/utils/extractContextMetadata.ts +14 -2
  79. package/src/enable-analytics.ts +19 -3
  80. package/src/index.ts +19 -0
  81. package/src/logExporters/fusionOTLPLogExporter/FusionOTLPLogExporter.ts +26 -4
  82. package/src/logExporters/fusionOTLPLogExporter/HttpClientExporterTransport.ts +36 -7
  83. package/src/logExporters/index.ts +8 -0
  84. package/src/module.ts +15 -15
  85. package/src/types.ts +39 -8
  86. package/src/version.ts +1 -1
@@ -2,11 +2,25 @@ import type { IAnalyticsAdapter } from './AnalyticsAdapter.interface.js';
2
2
  import type { AnalyticsEvent } from '../types.js';
3
3
 
4
4
  /**
5
- * An analytics adapter printing the events to the console.
5
+ * Analytics adapter that logs every event to the browser console.
6
+ *
7
+ * @remarks
8
+ * Useful during development and debugging. Each event is logged with the
9
+ * prefix `Analytics::Adapter::Console`.
10
+ *
11
+ * @template T - Analytics event type, defaults to {@link AnalyticsEvent}.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { ConsoleAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/adapters';
16
+ *
17
+ * builder.setAdapter('console', async () => new ConsoleAnalyticsAdapter());
18
+ * ```
6
19
  */
7
20
  export class ConsoleAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
8
21
  implements IAnalyticsAdapter
9
22
  {
23
+ /** Logs the event to the console. */
10
24
  registerAnalytic(event: T): Promise<void> | void {
11
25
  console.log('Analytics::Adapter::Console', event);
12
26
  }
@@ -18,10 +18,30 @@ import { version } from '../version.js';
18
18
  import { v7 as uuid } from 'uuid';
19
19
 
20
20
  /**
21
- * An analytics adapter for sending events to the log exporter for handling.
21
+ * Analytics adapter that forwards events to an OpenTelemetry log exporter.
22
22
  *
23
- * The exporter is a Open Telemetry exporter.
24
- * @see OTLPExporterBase
23
+ * @remarks
24
+ * Uses `@opentelemetry/sdk-logs` to batch and export log records through the
25
+ * provided {@link OTLPExporterBase} transport. Each analytics event is mapped
26
+ * to an OTLP `LogRecord` with severity `INFO`.
27
+ *
28
+ * Resource attributes automatically include the module version, a unique
29
+ * session ID (UUIDv7), and the portal ID supplied at construction time.
30
+ *
31
+ * @template T - Analytics event type, defaults to {@link AnalyticsEvent}.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { FusionAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/adapters';
36
+ * import { OTLPLogExporter } from '@equinor/fusion-framework-module-analytics/logExporters';
37
+ *
38
+ * builder.setAdapter('fusion-log', async () => {
39
+ * const logExporter = new OTLPLogExporter({ url: '/v1/logs' });
40
+ * return new FusionAnalyticsAdapter({ portalId: 'my-portal', logExporter });
41
+ * });
42
+ * ```
43
+ *
44
+ * @see {@link OTLPExporterBase}
25
45
  */
26
46
  export class FusionAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
27
47
  implements IAnalyticsAdapter
@@ -30,6 +50,13 @@ export class FusionAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
30
50
  #loggerProvider: LoggerProvider;
31
51
  #logger: Logger;
32
52
 
53
+ /**
54
+ * Creates a new `FusionAnalyticsAdapter`.
55
+ *
56
+ * @param args - Construction options.
57
+ * @param args.portalId - Portal identifier attached to every exported log record.
58
+ * @param args.logExporter - An OTLP-compatible log exporter for transport.
59
+ */
33
60
  constructor(args: { portalId: string; logExporter: OTLPExporterBase<ReadableLogRecord[]> }) {
34
61
  this.#logExporter = args.logExporter;
35
62
 
@@ -44,6 +71,7 @@ export class FusionAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
44
71
  this.#logger = this.#loggerProvider.getLogger('fusion');
45
72
  }
46
73
 
74
+ /** Maps an analytics event to an OTLP `LogRecord` and emits it via the logger. */
47
75
  registerAnalytic(event: T): Promise<void> | void {
48
76
  const logRecord: Partial<LogRecord> = {
49
77
  eventName: event.name,
@@ -54,6 +82,7 @@ export class FusionAnalyticsAdapter<T extends AnalyticsEvent = AnalyticsEvent>
54
82
  this.#logger.emit(logRecord);
55
83
  }
56
84
 
85
+ /** Shuts down the log exporter and logger provider, flushing remaining records. */
57
86
  [Symbol.dispose]() {
58
87
  this.#logExporter.shutdown();
59
88
  this.#loggerProvider.shutdown();
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Re-exports for built-in analytics adapters.
3
+ *
4
+ * @remarks
5
+ * Import from `'@equinor/fusion-framework-module-analytics/adapters'`.
6
+ *
7
+ * @packageDocumentation
8
+ */
1
9
  export { IAnalyticsAdapter } from './AnalyticsAdapter.interface.js';
2
10
  export { ConsoleAnalyticsAdapter } from './ConsoleAnalyticsAdapter.js';
3
11
  export { FusionAnalyticsAdapter } from './FusionAnalyticsAdapter.js';
@@ -2,9 +2,18 @@ import type { Subscribable } from 'rxjs';
2
2
  import type { AnalyticsEvent } from '../types.js';
3
3
 
4
4
  /**
5
- * Interface representing an analytics collector responsible for sending analytics events.
5
+ * Contract for an analytics collector that observes application state and emits
6
+ * structured analytics events.
6
7
  *
7
- * @template T - The type of analytics event handled by the adapter. Defaults to `AnalyticsEvent`.
8
+ * @remarks
9
+ * Implement this interface (or extend {@link BaseCollector}) to create a custom
10
+ * collector. Register it via {@link IAnalyticsConfigurator.setCollector}.
11
+ *
12
+ * The collector must be `Subscribable` so the provider can listen for emitted
13
+ * events. Optionally implement `initialize` for async setup (e.g. resolving
14
+ * module dependencies).
15
+ *
16
+ * @template T - Analytics event type emitted by this collector, defaults to {@link AnalyticsEvent}.
8
17
  */
9
18
  export interface IAnalyticsCollector<T extends AnalyticsEvent = AnalyticsEvent>
10
19
  extends Subscribable<T> {
@@ -20,14 +20,26 @@ import type { ContextModule } from '@equinor/fusion-framework-module-context';
20
20
 
21
21
  const EVENT_NAME = 'onAppModulesLoaded';
22
22
 
23
- /**
24
- * The schema of the data to be sent.
25
- */
23
+ /** Zod schema for the `app-loaded` event (value + attributes). */
26
24
  const eventSchema = createSchema(appSchema, z.object({ context: contextSchema }));
27
25
 
28
26
  /**
29
- * Collector to listen for app loaded and add values and attributes for
30
- * further processing by adapters.
27
+ * Collector that emits an analytics event whenever an application’s modules
28
+ * finish loading.
29
+ *
30
+ * @remarks
31
+ * Listens to the framework `onAppModulesLoaded` event, extracts application
32
+ * manifest metadata, and includes the current context (if available) in the
33
+ * event attributes.
34
+ *
35
+ * Register via:
36
+ * ```ts
37
+ * builder.setCollector('app-loaded', async (args) => {
38
+ * const event = await args.requireInstance('event');
39
+ * const app = await args.requireInstance('app');
40
+ * return new AppLoadedCollector(event, app);
41
+ * });
42
+ * ```
31
43
  */
32
44
  export class AppLoadedCollector
33
45
  extends BaseCollector<AppItemType, { context?: ContextItemType }>
@@ -36,6 +48,10 @@ export class AppLoadedCollector
36
48
  #eventProvider: IEventModuleProvider;
37
49
  #appProvider: AppModuleProvider;
38
50
 
51
+ /**
52
+ * @param eventProvider - Fusion event module provider to listen for app-loaded events.
53
+ * @param appProvider - Fusion app module provider for fallback manifest data.
54
+ */
39
55
  constructor(eventProvider: IEventModuleProvider, appProvider: AppModuleProvider) {
40
56
  super('app-loaded', eventSchema);
41
57
  this.#eventProvider = eventProvider;
@@ -11,14 +11,23 @@ import type { AppModuleProvider } from '@equinor/fusion-framework-module-app';
11
11
 
12
12
  import { z } from 'zod';
13
13
 
14
- /**
15
- * The schema of the data to be sent.
16
- */
14
+ /** Zod schema for the `app-selected` event (value + attributes). */
17
15
  const eventSchema = createSchema(appKeySchema, z.object({ previous: appKeySchema }));
18
16
 
19
17
  /**
20
- * Collector to listen for app selection and add values and attributes for
21
- * further processing by adapters.
18
+ * Collector that emits an analytics event whenever the active Fusion application changes.
19
+ *
20
+ * @remarks
21
+ * Listens to `AppModuleProvider.current$`, pairs consecutive values, and emits
22
+ * both the new and previous app key metadata.
23
+ *
24
+ * Register via:
25
+ * ```ts
26
+ * builder.setCollector('app-selected', async (args) => {
27
+ * const app = await args.requireInstance('app');
28
+ * return new AppSelectedCollector(app);
29
+ * });
30
+ * ```
22
31
  */
23
32
  export class AppSelectedCollector
24
33
  extends BaseCollector<AppKeyType, { previous?: AppKeyType }>
@@ -26,6 +35,9 @@ export class AppSelectedCollector
26
35
  {
27
36
  #appProvider: AppModuleProvider;
28
37
 
38
+ /**
39
+ * @param appProvider - Fusion app module provider to observe.
40
+ */
29
41
  constructor(appProvider: AppModuleProvider) {
30
42
  super('app-selected', eventSchema);
31
43
  this.#appProvider = appProvider;
@@ -5,7 +5,14 @@ import { from, map, type ObservableInput, Subject, type Observer, type Unsubscri
5
5
  import { z } from 'zod';
6
6
 
7
7
  /**
8
- * Function to create zod schema to be used when collecting
8
+ * Creates a Zod schema for validating {@link AnalyticsEvent} instances with
9
+ * typed value and attributes.
10
+ *
11
+ * @template TValue - Schema type for the event body.
12
+ * @template TAttr - Schema type for the event attributes.
13
+ * @param value - Zod schema validating the event body.
14
+ * @param attributes - Zod schema validating the event attributes.
15
+ * @returns A Zod object schema matching `{ name, value, attributes? }`.
9
16
  */
10
17
  export const createSchema = <TValue = AnyValue, TAttr = AnyValueMap>(
11
18
  value: z.ZodSchema<TValue>,
@@ -18,13 +25,35 @@ export const createSchema = <TValue = AnyValue, TAttr = AnyValueMap>(
18
25
  });
19
26
  };
20
27
 
28
+ /** Inferred Zod schema type produced by {@link createSchema}. */
21
29
  export type CollectorSchema<TValue = AnyValue, TAttr = AnyValueMap> = ReturnType<
22
30
  typeof createSchema<TValue, TAttr>
23
31
  >;
24
32
 
25
33
  /**
26
- * A base collector to help with parsing the events with provided schema before
27
- * emitting.
34
+ * Abstract base class for analytics collectors that validates events against a
35
+ * Zod schema before emitting.
36
+ *
37
+ * @remarks
38
+ * Subclasses implement {@link BaseCollector._initialize | _initialize} to return
39
+ * an observable of raw `{ value, attributes }` objects. `BaseCollector` wraps
40
+ * each emission with the collector name, validates it against the supplied schema,
41
+ * and publishes through an internal `Subject`.
42
+ *
43
+ * @template TValue - The event body type.
44
+ * @template TAttr - The event attributes type.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * class MyCollector extends BaseCollector<string, { page: string }> {
49
+ * constructor() {
50
+ * super('my-event', createSchema(z.string(), z.object({ page: z.string() })));
51
+ * }
52
+ * _initialize() {
53
+ * return of({ value: 'hello', attributes: { page: '/home' } });
54
+ * }
55
+ * }
56
+ * ```
28
57
  */
29
58
  export abstract class BaseCollector<
30
59
  TValue extends AnyValue,
@@ -37,13 +66,33 @@ export abstract class BaseCollector<
37
66
  z.infer<CollectorSchema<TValue, TAttr>>
38
67
  >();
39
68
 
69
+ /**
70
+ * Creates a new `BaseCollector`.
71
+ *
72
+ * @param name - Event name assigned to every emission (e.g. `'context-selected'`).
73
+ * @param schema - Zod schema used to validate each event before publishing.
74
+ */
40
75
  constructor(name: string, schema: CollectorSchema<TValue, TAttr>) {
41
76
  this.#name = name;
42
77
  this.#schema = schema;
43
78
  }
44
79
 
80
+ /**
81
+ * Returns an observable source of raw `{ value, attributes }` objects.
82
+ *
83
+ * @remarks
84
+ * Subclasses implement this to provide the domain-specific event stream.
85
+ * The base class wraps the output with the collector name and validates it.
86
+ *
87
+ * @returns An observable input of value/attribute pairs.
88
+ */
45
89
  abstract _initialize(): ObservableInput<{ value: TValue; attributes: TAttr }>;
46
90
 
91
+ /**
92
+ * Subscribes to the source returned by {@link BaseCollector._initialize},
93
+ * validates each emission with the Zod schema, and publishes through the
94
+ * internal subject.
95
+ */
47
96
  initialize(): Promise<void> | void {
48
97
  from(this._initialize())
49
98
  .pipe(
@@ -59,6 +108,12 @@ export abstract class BaseCollector<
59
108
  .subscribe(this.#subject);
60
109
  }
61
110
 
111
+ /**
112
+ * Subscribes an observer to the validated event stream.
113
+ *
114
+ * @param observer - Partial observer receiving validated events.
115
+ * @returns An `Unsubscribable` handle.
116
+ */
62
117
  subscribe(observer: Partial<Observer<z.infer<CollectorSchema<TValue, TAttr>>>>): Unsubscribable {
63
118
  return this.#subject.subscribe(observer);
64
119
  }
@@ -10,17 +10,28 @@ import {
10
10
  extractContextMetadata,
11
11
  } from './utils/extractContextMetadata.js';
12
12
 
13
- /**
14
- * The schema of the data to be sent.
15
- */
13
+ /** Zod schema for the `context-selected` event (value + attributes). */
16
14
  const eventSchema = createSchema(
17
15
  contextSchema,
18
16
  z.object({ previous: contextSchema, appKey: z.string().optional() }),
19
17
  );
20
18
 
21
19
  /**
22
- * Collector to listen for context changes and add values and attributes for
23
- * further processing by adapters.
20
+ * Collector that emits an analytics event whenever the active Fusion context changes.
21
+ *
22
+ * @remarks
23
+ * Listens to `IContextProvider.currentContext$`, de-duplicates by context ID,
24
+ * pairs consecutive values, and emits both the new and previous context
25
+ * metadata. The current application key is included in attributes.
26
+ *
27
+ * Register via:
28
+ * ```ts
29
+ * builder.setCollector('context-selected', async (args) => {
30
+ * const ctx = await args.requireInstance('context');
31
+ * const app = await args.requireInstance('app');
32
+ * return new ContextSelectedCollector(ctx, app);
33
+ * });
34
+ * ```
24
35
  */
25
36
  export class ContextSelectedCollector
26
37
  extends BaseCollector<ContextItemType, { previous?: ContextItemType; appKey?: string }>
@@ -29,6 +40,10 @@ export class ContextSelectedCollector
29
40
  #contextProvider: IContextProvider;
30
41
  #appProvider: AppModuleProvider;
31
42
 
43
+ /**
44
+ * @param contextProvider - Fusion context module provider to observe.
45
+ * @param appProvider - Fusion app module provider for the current app key.
46
+ */
32
47
  constructor(contextProvider: IContextProvider, appProvider: AppModuleProvider) {
33
48
  super('context-selected', eventSchema);
34
49
  this.#contextProvider = contextProvider;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Re-exports for built-in analytics collectors.
3
+ *
4
+ * @remarks
5
+ * Import from `'@equinor/fusion-framework-module-analytics/collectors'`.
6
+ *
7
+ * @packageDocumentation
8
+ */
1
9
  export { IAnalyticsCollector } from './AnalyticsCollector.interface.js';
2
10
  export { ContextSelectedCollector } from './ContextSelectedCollector.js';
3
11
  export { AppSelectedCollector } from './AppSelectedCollector.js';
@@ -1,8 +1,12 @@
1
1
  import { z } from 'zod';
2
2
  import type { AppManifest, CurrentApp } from '@equinor/fusion-framework-module-app';
3
3
 
4
- // Schema representing an object with key appKey and a string value.
5
- // Used to parse an object containing appKey.
4
+ /**
5
+ * Zod schema for an object containing an optional `appKey` string.
6
+ *
7
+ * @remarks
8
+ * Used by {@link AppSelectedCollector} to validate the event body.
9
+ */
6
10
  export const appKeySchema = z
7
11
  .object({
8
12
  appKey: z.string().optional(),
@@ -10,8 +14,13 @@ export const appKeySchema = z
10
14
  .optional()
11
15
  .nullable();
12
16
 
13
- // Schema representing an object with data points of an app.
14
- // Used to parse an object with app data.
17
+ /**
18
+ * Zod schema for a Fusion application metadata object.
19
+ *
20
+ * @remarks
21
+ * Validates core app fields (appKey, displayName, type) and optional build
22
+ * and category information. Used by {@link AppLoadedCollector}.
23
+ */
15
24
  export const appSchema = z
16
25
  .object({
17
26
  appKey: z.string(),
@@ -23,16 +32,30 @@ export const appSchema = z
23
32
  })
24
33
  .optional();
25
34
 
35
+ /** Inferred type from {@link appKeySchema}. */
26
36
  export type AppKeyType = z.infer<typeof appKeySchema>;
27
37
 
38
+ /** Inferred type from {@link appSchema}. */
28
39
  export type AppItemType = z.infer<typeof appSchema>;
29
40
 
41
+ /**
42
+ * Extracts app-key metadata from a `CurrentApp` instance.
43
+ *
44
+ * @param app - The current app object.
45
+ * @returns An object containing the optional `appKey`.
46
+ */
30
47
  export const extractAppKeyMetadata = (app: CurrentApp): z.input<typeof appKeySchema> => {
31
48
  return {
32
49
  appKey: app?.appKey,
33
50
  };
34
51
  };
35
52
 
53
+ /**
54
+ * Extracts detailed app metadata from an `AppManifest` for analytics events.
55
+ *
56
+ * @param app - The application manifest.
57
+ * @returns An object with appKey, displayName, type, and optional build/category info.
58
+ */
36
59
  export const extractAppMetadata = (app: AppManifest): z.input<typeof appSchema> => {
37
60
  return {
38
61
  appKey: app.appKey,
@@ -1,8 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  import type { ContextItem } from '@equinor/fusion-framework-module-context';
3
3
 
4
- // Schema representing an object with data points of a Fusion context.
5
- // Used to parse an object with context data.
4
+ /**
5
+ * Zod schema for a Fusion context metadata object.
6
+ *
7
+ * @remarks
8
+ * Validates core context fields (id, type) and optional title, externalId,
9
+ * and source. Used by {@link ContextSelectedCollector} and {@link AppLoadedCollector}.
10
+ */
6
11
  export const contextSchema = z
7
12
  .object({
8
13
  id: z.string(),
@@ -14,8 +19,15 @@ export const contextSchema = z
14
19
  .optional()
15
20
  .nullable();
16
21
 
22
+ /** Inferred type from {@link contextSchema}. */
17
23
  export type ContextItemType = z.infer<typeof contextSchema>;
18
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
+ */
19
31
  export const extractContextMetadata = (context: ContextItem): z.input<typeof contextSchema> => {
20
32
  return {
21
33
  id: context.id,
@@ -3,13 +3,29 @@ import type { IModulesConfigurator } from '@equinor/fusion-framework-module';
3
3
  import { module } from './module.js';
4
4
  import type { IAnalyticsConfigurator } from './AnalyticsConfigurator.interface.js';
5
5
 
6
+ /** Callback invoked with the {@link IAnalyticsConfigurator} to register adapters and collectors. */
6
7
  type AnalyticsBuilderCallback = (builder: IAnalyticsConfigurator) => void | Promise<void>;
7
8
 
8
9
  /**
9
- * Enables analytics for agiven module configurator.
10
+ * Enables the analytics module on a Fusion Framework module configurator.
10
11
  *
11
- * @param configurator - The module configurator instance to which analytics should be attached.
12
- * @param callback - An optional callback to further configure the analytics builder. Can be synchronous or asynchronous.
12
+ * Call this helper during application or portal configuration to register
13
+ * adapters and collectors for analytics tracking.
14
+ *
15
+ * @param configurator - The module configurator instance to attach analytics to.
16
+ * @param callback - Optional callback to register adapters and collectors on the analytics builder.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { enableAnalytics } from '@equinor/fusion-framework-module-analytics';
21
+ * import { ConsoleAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/adapters';
22
+ *
23
+ * const configure = (configurator) => {
24
+ * enableAnalytics(configurator, (builder) => {
25
+ * builder.setAdapter('console', async () => new ConsoleAnalyticsAdapter());
26
+ * });
27
+ * };
28
+ * ```
13
29
  */
14
30
  export const enableAnalytics = (
15
31
  // biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
package/src/index.ts CHANGED
@@ -1,3 +1,22 @@
1
+ /**
2
+ * `@equinor/fusion-framework-module-analytics` — Fusion Framework module for
3
+ * collecting and exporting application analytics using OpenTelemetry standards.
4
+ *
5
+ * @remarks
6
+ * This package provides a pluggable adapter/collector architecture:
7
+ *
8
+ * - **Adapters** receive analytics events and forward them to a backend
9
+ * (e.g. console, OTLP endpoint).
10
+ * - **Collectors** observe application state (context changes, app loads) and
11
+ * emit structured {@link AnalyticsEvent} instances.
12
+ *
13
+ * Import adapters from `'@equinor/fusion-framework-module-analytics/adapters'`,
14
+ * collectors from `'@equinor/fusion-framework-module-analytics/collectors'`,
15
+ * and log exporters from `'@equinor/fusion-framework-module-analytics/logExporters'`.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+
1
20
  export { module as analyticsModule, AnalyticsModule } from './module.js';
2
21
 
3
22
  export { AnalyticsEvent, AnyValue, AnyValueMap } from './types.js';
@@ -11,7 +11,11 @@ import {
11
11
  import type { IHttpClient } from '@equinor/fusion-framework-module-http';
12
12
  import { HttpClientExporterTransport } from './HttpClientExporterTransport.js';
13
13
 
14
- // The shared default configuration needed for the OTLPNetworkExportDelegate
14
+ /**
15
+ * Returns shared default OTLP exporter configuration values.
16
+ *
17
+ * @returns Default timeout, concurrency limit, and compression settings.
18
+ */
15
19
  function getSharedConfigurationDefaults(): OtlpSharedConfiguration {
16
20
  return {
17
21
  timeoutMillis: 10000,
@@ -21,14 +25,32 @@ function getSharedConfigurationDefaults(): OtlpSharedConfiguration {
21
25
  }
22
26
 
23
27
  /**
24
- * A log exporter extending OTLPExporterBase but substitute the normal transport
25
- * with a HttpClientExporterTransport. This will use the provided httpClient to
26
- * emit the event.
28
+ * OTLP log exporter that uses a Fusion `IHttpClient` as its HTTP transport.
29
+ *
30
+ * @remarks
31
+ * Extends `OTLPExporterBase` and substitutes the default `fetch`-based transport
32
+ * with {@link HttpClientExporterTransport}, routing log record payloads
33
+ * through the Fusion HTTP module’s configured client (which may include
34
+ * authentication headers, interceptors, and service‑discovery routing).
35
+ *
36
+ * Typically used with {@link FusionAnalyticsAdapter} when the portal provides
37
+ * an HTTP client via service discovery.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { FusionOTLPLogExporter } from '@equinor/fusion-framework-module-analytics/logExporters';
42
+ *
43
+ * const httpClient = await serviceDiscovery.createClient('analytics');
44
+ * const exporter = new FusionOTLPLogExporter(httpClient);
45
+ * ```
27
46
  */
28
47
  export class FusionOTLPLogExporter
29
48
  extends OTLPExporterBase<ReadableLogRecord[]>
30
49
  implements LogRecordExporter
31
50
  {
51
+ /**
52
+ * @param httpClient - A Fusion `IHttpClient` used for outbound HTTP transport.
53
+ */
32
54
  constructor(httpClient: IHttpClient) {
33
55
  super(
34
56
  createOtlpNetworkExportDelegate(
@@ -2,6 +2,12 @@ import type { IExporterTransport, ExportResponse } from '@opentelemetry/otlp-exp
2
2
 
3
3
  import type { IHttpClient } from '@equinor/fusion-framework-module-http';
4
4
 
5
+ /**
6
+ * Checks whether a failed HTTP status code is eligible for automatic retry.
7
+ *
8
+ * @param statusCode - HTTP response status code.
9
+ * @returns `true` for 429, 502, 503, and 504.
10
+ */
5
11
  function isExportRetryable(statusCode: number): boolean {
6
12
  // Status codes of when we should consider retrying.
7
13
  // 429 Too Many Requests
@@ -12,7 +18,12 @@ function isExportRetryable(statusCode: number): boolean {
12
18
  return retryCodes.includes(statusCode);
13
19
  }
14
20
 
15
- // Parse the `Retry-After` header and return when the service will allow a retry.
21
+ /**
22
+ * Parses an HTTP `Retry-After` header value into milliseconds.
23
+ *
24
+ * @param retryAfter - Raw header value (integer seconds or HTTP-date).
25
+ * @returns Delay in milliseconds, `-1` for non-positive integers, or `undefined` if absent.
26
+ */
16
27
  function parseRetryAfterToMills(retryAfter?: string | undefined | null): number | undefined {
17
28
  if (retryAfter == null) {
18
29
  return undefined;
@@ -32,19 +43,36 @@ function parseRetryAfterToMills(retryAfter?: string | undefined | null): number
32
43
  }
33
44
 
34
45
  /**
35
- * A Exporter Transport to POST events to provided path using the provided httpClient
46
+ * OpenTelemetry exporter transport that posts serialised log records using a
47
+ * Fusion `IHttpClient`.
48
+ *
49
+ * @remarks
50
+ * Implements `IExporterTransport` from `@opentelemetry/otlp-exporter-base`.
51
+ * The transport honours the exporter’s timeout via `AbortController` and
52
+ * inspects response status codes to report retryable failures (429, 502–504).
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const transport = new HttpClientExporterTransport(httpClient, '/v1/logs');
57
+ * ```
36
58
  */
37
59
  export class HttpClientExporterTransport implements IExporterTransport {
60
+ /**
61
+ * @param httpClient - Fusion HTTP client for outgoing requests.
62
+ * @param path - URL path appended to the client’s base URL (default `'/v1/logs'`).
63
+ */
38
64
  constructor(
39
65
  private httpClient: IHttpClient,
40
66
  private path: string = '/v1/logs',
41
67
  ) {}
42
68
 
43
- // Will send data with the httpClient.
44
- // If the service responds with a non 2** statusCode, we check if it is
45
- // retryable.
46
- // The timeoutMillis determines when to abort if the service has not yet
47
- // responded.
69
+ /**
70
+ * Sends serialised log record data to the configured endpoint.
71
+ *
72
+ * @param data - Serialised OTLP payload as a `Uint8Array`.
73
+ * @param timeoutMillis - Maximum time in milliseconds before the request is aborted.
74
+ * @returns An `ExportResponse` indicating success, retryable failure, or permanent failure.
75
+ */
48
76
  async send(data: Uint8Array, timeoutMillis: number): Promise<ExportResponse> {
49
77
  const abortController = new AbortController();
50
78
  const timeout = setTimeout(() => abortController.abort(), timeoutMillis);
@@ -83,6 +111,7 @@ export class HttpClientExporterTransport implements IExporterTransport {
83
111
  clearTimeout(timeout);
84
112
  }
85
113
  }
114
+ /** No-op; the HTTP client does not hold persistent connections. */
86
115
  shutdown(): void {
87
116
  // intentionally left empty, nothing to do.
88
117
  }
@@ -1,2 +1,10 @@
1
+ /**
2
+ * Re-exports for OTLP log exporters used by analytics adapters.
3
+ *
4
+ * @remarks
5
+ * Import from `'@equinor/fusion-framework-module-analytics/logExporters'`.
6
+ *
7
+ * @packageDocumentation
8
+ */
1
9
  export { FusionOTLPLogExporter } from './fusionOTLPLogExporter/FusionOTLPLogExporter.js';
2
10
  export { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';