@momentumcms/plugins-otel 0.5.4 → 0.5.5

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/package.json CHANGED
@@ -1,37 +1,62 @@
1
1
  {
2
- "name": "@momentumcms/plugins-otel",
3
- "version": "0.5.4",
4
- "description": "OpenTelemetry observability plugin for Momentum CMS",
5
- "license": "MIT",
6
- "author": "Momentum CMS Contributors",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/DonaldMurillo/momentum-cms.git",
10
- "directory": "libs/plugins/otel"
11
- },
12
- "homepage": "https://github.com/DonaldMurillo/momentum-cms#readme",
13
- "bugs": {
14
- "url": "https://github.com/DonaldMurillo/momentum-cms/issues"
15
- },
16
- "keywords": [
17
- "cms",
18
- "momentum-cms",
19
- "opentelemetry",
20
- "observability",
21
- "tracing",
22
- "metrics"
23
- ],
24
- "engines": {
25
- "node": ">=18"
26
- },
27
- "main": "./index.cjs",
28
- "types": "./src/index.d.ts",
29
- "peerDependencies": {
30
- "@momentumcms/core": "0.5.4",
31
- "@momentumcms/logger": "0.5.4",
32
- "@momentumcms/plugins-core": "0.5.4",
33
- "@opentelemetry/api": "^1.0.0"
34
- },
35
- "dependencies": {},
36
- "module": "./index.js"
37
- }
2
+ "name": "@momentumcms/plugins-otel",
3
+ "version": "0.5.5",
4
+ "description": "OpenTelemetry observability plugin for Momentum CMS",
5
+ "license": "MIT",
6
+ "author": "Momentum CMS Contributors",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/DonaldMurillo/momentum-cms.git",
10
+ "directory": "libs/plugins/otel"
11
+ },
12
+ "homepage": "https://github.com/DonaldMurillo/momentum-cms#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/DonaldMurillo/momentum-cms/issues"
15
+ },
16
+ "keywords": [
17
+ "cms",
18
+ "momentum-cms",
19
+ "opentelemetry",
20
+ "observability",
21
+ "tracing",
22
+ "metrics"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "main": "./index.cjs",
28
+ "types": "./src/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./src/index.d.ts",
32
+ "default": "./index.cjs"
33
+ },
34
+ "./admin-routes": {
35
+ "types": "./src/lib/otel-admin-routes.d.ts",
36
+ "default": "./lib/otel-admin-routes.cjs"
37
+ }
38
+ },
39
+ "peerDependencies": {
40
+ "@opentelemetry/api": "^1.3.0",
41
+ "@opentelemetry/sdk-metrics": "^2.0.0",
42
+ "@opentelemetry/exporter-prometheus": "^0.213.0",
43
+ "@momentumcms/plugins-core": "0.5.5",
44
+ "@momentumcms/logger": "0.5.5",
45
+ "@momentumcms/core": "0.5.5",
46
+ "express": "^4.21.2",
47
+ "@angular/core": "~21.2.0",
48
+ "@angular/common": "~21.2.0",
49
+ "@momentumcms/ui": "0.5.5",
50
+ "@ng-icons/core": "^33.0.0",
51
+ "@ng-icons/heroicons": "^33.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "@opentelemetry/sdk-metrics": {
55
+ "optional": true
56
+ },
57
+ "@opentelemetry/exporter-prometheus": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "dependencies": {}
62
+ }
package/src/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { otelPlugin } from './lib/otel-plugin';
2
- export type { OtelPluginConfig } from './lib/otel-plugin.types';
2
+ export type { OtelPluginConfig, OtelMetricsConfig, OtelSummaryData, OtelSnapshotData, SpanRecord, } from './lib/otel-plugin.types';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared type guards and interfaces for OTel API consumers.
3
+ *
4
+ * Used by both the query handler and the snapshot service to
5
+ * duck-type the MomentumAPI and collection operations.
6
+ */
7
+ export interface MomentumAPILike {
8
+ collection(slug: string): unknown;
9
+ setContext(ctx: Record<string, unknown>): MomentumAPILike;
10
+ }
11
+ export interface FindableCollection {
12
+ find(query: Record<string, unknown>): Promise<{
13
+ docs?: unknown[];
14
+ totalDocs?: number;
15
+ totalPages?: number;
16
+ }>;
17
+ }
18
+ export interface CreatableCollection {
19
+ create(data: Record<string, unknown>): Promise<unknown>;
20
+ }
21
+ export interface DeletableCollection {
22
+ delete(id: string): Promise<unknown>;
23
+ }
24
+ export declare function isFindable(val: unknown): val is FindableCollection;
25
+ export declare function isCreatable(val: unknown): val is CreatableCollection;
26
+ export declare function isDeletable(val: unknown): val is DeletableCollection;
27
+ export declare function isRecord(val: unknown): val is Record<string, unknown>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * OTel Query Handler
3
+ *
4
+ * Express Router that serves the observability summary, history,
5
+ * export, and purge endpoints for the admin dashboard.
6
+ * Admin-gated using type guards (no assertions).
7
+ */
8
+ import type { Router } from 'express';
9
+ import type { MetricsStore } from '../metrics/metrics-store';
10
+ import type { MetricsSnapshotService } from '../metrics/metrics-snapshot-service';
11
+ import { type MomentumAPILike } from './otel-api-guards';
12
+ /**
13
+ * Creates an Express Router that serves OTel endpoints.
14
+ *
15
+ * `GET /api/otel/summary` — live metrics snapshot
16
+ * `GET /api/otel/history` — persisted snapshots with time range
17
+ * `DELETE /api/otel/history` — purge all snapshots
18
+ * `GET /api/otel/export` — streaming CSV download
19
+ */
20
+ export declare function createOtelQueryRouter(store: MetricsStore, getApi?: () => MomentumAPILike | null, snapshotService?: MetricsSnapshotService | null): Router;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Prometheus Scrape Endpoint
3
+ *
4
+ * Express Router that exposes metrics in Prometheus text exposition format.
5
+ * Uses @opentelemetry/exporter-prometheus in manual mode (no standalone server).
6
+ */
7
+ import type { Router } from 'express';
8
+ interface PrometheusHandlerOptions {
9
+ /** The PrometheusExporter instance (from @opentelemetry/exporter-prometheus) */
10
+ exporter: unknown;
11
+ }
12
+ /**
13
+ * Creates an Express Router that serves the Prometheus scrape endpoint.
14
+ *
15
+ * The endpoint is intentionally unauthenticated — Prometheus scrapers
16
+ * typically don't carry auth tokens, and metrics data is operational
17
+ * (not sensitive user data).
18
+ */
19
+ export declare function createPrometheusHandler(options: PrometheusHandlerOptions): Router;
20
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Collection Operation Metrics
3
+ *
4
+ * Injects hooks into collections that record OTel metrics (counters, histograms)
5
+ * and update the internal MetricsStore for each CRUD operation.
6
+ */
7
+ import type { CollectionConfig } from '@momentumcms/core';
8
+ import type { Meter } from '@opentelemetry/api';
9
+ import type { MetricsStore } from './metrics-store';
10
+ interface CollectionMetricsOptions {
11
+ store: MetricsStore;
12
+ /** OTel Meter instance (optional) */
13
+ meter?: Meter | null;
14
+ /** Filter to specific operations */
15
+ operations?: string[];
16
+ }
17
+ /**
18
+ * Injects collection hooks that record operation metrics.
19
+ */
20
+ export declare function injectCollectionMetricsHooks(collections: CollectionConfig[], options: CollectionMetricsOptions): void;
21
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Metrics Snapshot Service
3
+ *
4
+ * Periodically persists in-memory metrics to the otel-snapshots collection
5
+ * and restores them on startup. Auto-prunes old snapshots to bound DB growth.
6
+ */
7
+ import type { MetricsStore } from './metrics-store';
8
+ import { type MomentumAPILike } from '../api/otel-api-guards';
9
+ export type { MomentumAPILike } from '../api/otel-api-guards';
10
+ export interface MetricsSnapshotServiceOptions {
11
+ store: MetricsStore;
12
+ getApi: () => MomentumAPILike | null;
13
+ snapshotInterval?: number;
14
+ retentionDays?: number;
15
+ }
16
+ export declare class MetricsSnapshotService {
17
+ private readonly store;
18
+ private readonly getApi;
19
+ private readonly snapshotInterval;
20
+ private readonly retentionDays;
21
+ private readonly logger;
22
+ private timer;
23
+ private flushing;
24
+ constructor(options: MetricsSnapshotServiceOptions);
25
+ start(): void;
26
+ flush(): Promise<void>;
27
+ restore(): Promise<void>;
28
+ shutdown(): Promise<void>;
29
+ purgeAll(): Promise<number>;
30
+ private prune;
31
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * In-memory metrics store for the admin dashboard API.
3
+ *
4
+ * Maintains rolling counters and a circular buffer of recent spans
5
+ * so the `/api/otel/summary` endpoint can return a JSON snapshot
6
+ * without directly depending on the OTel SDK.
7
+ */
8
+ import type { SpanRecord, OtelSummaryData, OtelSnapshotData } from '../otel-plugin.types';
9
+ export type { SpanRecord, OtelSummaryData, OtelSnapshotData } from '../otel-plugin.types';
10
+ export declare class MetricsStore {
11
+ private readonly startTime;
12
+ private readonly maxSpans;
13
+ private readonly recentSpans;
14
+ private readonly collectionMetrics;
15
+ private activeRequests;
16
+ private totalRequests;
17
+ private totalDurationMs;
18
+ private errorCount;
19
+ private readonly byMethod;
20
+ private readonly byStatusCode;
21
+ constructor(maxSpans?: number);
22
+ recordSpan(span: SpanRecord): void;
23
+ recordCollectionOperation(collection: string, operation: 'create' | 'update' | 'delete', durationMs: number): void;
24
+ recordHttpRequest(method: string, statusCode: number, durationMs: number): void;
25
+ incrementActiveRequests(): void;
26
+ decrementActiveRequests(): void;
27
+ getSnapshotData(): OtelSnapshotData;
28
+ restore(snapshot: OtelSnapshotData): void;
29
+ private buildCollectionMetrics;
30
+ getSummary(): OtelSummaryData;
31
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Shared OTel helpers.
3
+ *
4
+ * Uses the @opentelemetry/api types (already a required peer dep)
5
+ * to provide properly typed instrument creation without type assertions.
6
+ */
7
+ import type { Meter, Counter, Histogram, UpDownCounter } from '@opentelemetry/api';
8
+ export interface OtelInstruments {
9
+ requestDuration: Histogram;
10
+ requestTotal: Counter;
11
+ activeRequests: UpDownCounter;
12
+ }
13
+ export interface CollectionInstruments {
14
+ operationTotal: Counter;
15
+ operationDuration: Histogram;
16
+ }
17
+ /**
18
+ * Creates HTTP request instruments from a Meter.
19
+ */
20
+ export declare function createRequestInstruments(meter: Meter): OtelInstruments;
21
+ /**
22
+ * Creates collection operation instruments from a Meter.
23
+ */
24
+ export declare function createCollectionInstruments(meter: Meter): CollectionInstruments;
25
+ /**
26
+ * Tries to load the OTel SDK and Prometheus exporter.
27
+ * Returns null if the packages are not installed.
28
+ */
29
+ export declare function tryLoadOtelSdk(serviceName: string): {
30
+ meter: Meter;
31
+ provider: {
32
+ shutdown: () => Promise<void>;
33
+ };
34
+ exporter: unknown;
35
+ } | null;
36
+ /**
37
+ * Reads span context from a doc's __otelSpan property.
38
+ */
39
+ export declare function getSpanContext(doc: Record<string, unknown>): {
40
+ traceId: string;
41
+ spanId: string;
42
+ } | null;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OTel Snapshots Collection
3
+ *
4
+ * Internal collection for persisting periodic metric snapshots.
5
+ * Injected into the collections array by the otel plugin during onInit.
6
+ */
7
+ export declare const OtelSnapshotsCollection: import("@momentumcms/core").CollectionConfig;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * HTTP Request Metrics Middleware
3
+ *
4
+ * Express middleware that records HTTP request metrics using both
5
+ * the OpenTelemetry Metrics API and the internal MetricsStore.
6
+ */
7
+ import type { Router } from 'express';
8
+ import type { Meter } from '@opentelemetry/api';
9
+ import type { MetricsStore } from './metrics-store';
10
+ interface RequestMetricsOptions {
11
+ store: MetricsStore;
12
+ /** OTel Meter instance (optional — when provided, records OTel instruments) */
13
+ meter?: Meter | null;
14
+ }
15
+ /**
16
+ * Creates Express middleware that records HTTP request metrics.
17
+ */
18
+ export declare function createRequestMetricsMiddleware(options: RequestMetricsOptions): Router;
19
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { PluginAdminRouteDescriptor } from '@momentumcms/core';
2
+
3
+ export declare const otelAdminRoutes: PluginAdminRouteDescriptor[];
@@ -1,12 +1,13 @@
1
1
  /**
2
- * OpenTelemetry Tracing Plugin
2
+ * OpenTelemetry Observability Plugin
3
3
  *
4
- * Injects tracing spans into collection hooks and optionally
5
- * enriches log entries with trace/span IDs.
4
+ * Injects tracing spans into collection hooks, optionally enriches log
5
+ * entries with trace/span IDs, and can collect metrics exposed via a
6
+ * Prometheus scrape endpoint and an admin dashboard.
6
7
  *
7
8
  * Requires @opentelemetry/api as a peer dependency.
8
- * The user is responsible for setting up the OTel SDK (exporters, etc.).
9
- * This plugin only creates spans using the OTel API.
9
+ * Metrics features require @opentelemetry/sdk-metrics and optionally
10
+ * @opentelemetry/exporter-prometheus (both optional peer deps).
10
11
  *
11
12
  * @example
12
13
  * ```typescript
@@ -14,7 +15,10 @@
14
15
  *
15
16
  * export default defineMomentumConfig({
16
17
  * plugins: [
17
- * otelPlugin({ serviceName: 'my-cms' }),
18
+ * otelPlugin({
19
+ * serviceName: 'my-cms',
20
+ * metrics: { enabled: true, prometheus: true },
21
+ * }),
18
22
  * ],
19
23
  * });
20
24
  * ```
@@ -22,7 +26,7 @@
22
26
  import type { MomentumPlugin } from '@momentumcms/plugins/core';
23
27
  import type { OtelPluginConfig } from './otel-plugin.types';
24
28
  /**
25
- * Creates an OpenTelemetry tracing plugin.
29
+ * Creates an OpenTelemetry observability plugin.
26
30
  *
27
31
  * @param config - Plugin configuration
28
32
  * @returns MomentumPlugin instance
@@ -1,6 +1,82 @@
1
1
  /**
2
- * OTel Plugin Configuration Types
2
+ * OTel Plugin Types
3
+ *
4
+ * Shared types used by both server (metrics-store) and browser (dashboard service).
3
5
  */
6
+ /**
7
+ * A recorded span for the admin dashboard.
8
+ */
9
+ export interface SpanRecord {
10
+ traceId: string;
11
+ spanId: string;
12
+ name: string;
13
+ collection: string;
14
+ operation: string;
15
+ durationMs: number;
16
+ status: 'ok' | 'error';
17
+ timestamp: string;
18
+ }
19
+ /**
20
+ * Collection metrics entry for the admin dashboard.
21
+ */
22
+ export interface CollectionMetricEntry {
23
+ collection: string;
24
+ creates: number;
25
+ updates: number;
26
+ deletes: number;
27
+ avgDurationMs: number;
28
+ }
29
+ /**
30
+ * Full observability summary returned by the admin API.
31
+ */
32
+ export interface OtelSummaryData {
33
+ uptime: number;
34
+ activeRequests: number;
35
+ memoryUsageMb: number;
36
+ requestMetrics: {
37
+ totalRequests: number;
38
+ avgDurationMs: number;
39
+ errorCount: number;
40
+ byMethod: Record<string, number>;
41
+ byStatusCode: Record<string, number>;
42
+ };
43
+ collectionMetrics: CollectionMetricEntry[];
44
+ recentSpans: SpanRecord[];
45
+ }
46
+ /**
47
+ * A persisted metric snapshot (stored in the otel-snapshots collection).
48
+ */
49
+ export interface OtelSnapshotData {
50
+ id?: string;
51
+ totalRequests: number;
52
+ errorCount: number;
53
+ avgDurationMs: number;
54
+ memoryUsageMb: number;
55
+ byMethod: Record<string, number>;
56
+ byStatusCode: Record<string, number>;
57
+ collectionMetrics: CollectionMetricEntry[];
58
+ topSpans: SpanRecord[];
59
+ createdAt?: string;
60
+ }
61
+ /**
62
+ * Metrics configuration for the OpenTelemetry plugin.
63
+ */
64
+ export interface OtelMetricsConfig {
65
+ /** Enable metrics collection. @default false */
66
+ enabled?: boolean;
67
+ /** Prometheus scrape endpoint. @default true when metrics enabled */
68
+ prometheus?: boolean | {
69
+ path?: string;
70
+ };
71
+ /** Custom MeterProvider instance. When omitted and prometheus is enabled, one is auto-created. */
72
+ meterProvider?: unknown;
73
+ /** Admin observability dashboard. @default true when metrics enabled */
74
+ adminDashboard?: boolean;
75
+ /** Interval between snapshot flushes in ms. @default 60000 */
76
+ snapshotInterval?: number;
77
+ /** Days to retain snapshots before auto-pruning. @default 7 */
78
+ retentionDays?: number;
79
+ }
4
80
  /**
5
81
  * Configuration for the OpenTelemetry plugin.
6
82
  */
@@ -13,4 +89,6 @@ export interface OtelPluginConfig {
13
89
  attributes?: Record<string, string>;
14
90
  /** Collection operations to trace. @default all */
15
91
  operations?: Array<'create' | 'update' | 'delete' | 'find'>;
92
+ /** Metrics configuration */
93
+ metrics?: OtelMetricsConfig;
16
94
  }
package/CHANGELOG.md DELETED
@@ -1,110 +0,0 @@
1
- ## 0.5.4 (2026-03-07)
2
-
3
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
4
-
5
- ## 0.5.0 (2026-02-23)
6
-
7
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
8
-
9
- ## 0.4.1 (2026-02-22)
10
-
11
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
12
-
13
- ## 0.4.0 (2026-02-22)
14
-
15
- ### 🚀 Features
16
-
17
- - blocks showcase with articles, pages, and UI fixes ([#36](https://github.com/DonaldMurillo/momentum-cms/pull/36))
18
-
19
- ### ❤️ Thank You
20
-
21
- - Claude Opus 4.6
22
- - Donald Murillo @DonaldMurillo
23
-
24
- ## 0.3.0 (2026-02-20)
25
-
26
- ### 🚀 Features
27
-
28
- - add named tabs support with nested data grouping and UI improvements ([#30](https://github.com/DonaldMurillo/momentum-cms/pull/30))
29
-
30
- ### ❤️ Thank You
31
-
32
- - Claude Opus 4.6
33
- - Donald Murillo @DonaldMurillo
34
-
35
- ## 0.2.0 (2026-02-17)
36
-
37
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
38
-
39
- ## 0.1.10 (2026-02-17)
40
-
41
- ### 🩹 Fixes
42
-
43
- - **create-momentum-app:** add shell option to execFileSync for Windows ([#28](https://github.com/DonaldMurillo/momentum-cms/pull/28))
44
-
45
- ### ❤️ Thank You
46
-
47
- - Claude Opus 4.6
48
- - Donald Murillo @DonaldMurillo
49
-
50
- ## 0.1.9 (2026-02-16)
51
-
52
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
53
-
54
- ## 0.1.8 (2026-02-16)
55
-
56
- ### 🩹 Fixes
57
-
58
- - correct repository URLs and add GitHub link to CLI ([#26](https://github.com/DonaldMurillo/momentum-cms/pull/26))
59
-
60
- ### ❤️ Thank You
61
-
62
- - Claude Opus 4.6
63
- - Donald Murillo @DonaldMurillo
64
-
65
- ## 0.1.7 (2026-02-16)
66
-
67
- ### 🩹 Fixes
68
-
69
- - correct repository URLs and add GitHub link to CLI output ([f7e96bb](https://github.com/DonaldMurillo/momentum-cms/commit/f7e96bb))
70
-
71
- ### ❤️ Thank You
72
-
73
- - Claude Opus 4.6
74
- - Donald Murillo @DonaldMurillo
75
-
76
- ## 0.1.6 (2026-02-16)
77
-
78
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
79
-
80
- ## 0.1.5 (2026-02-16)
81
-
82
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
83
-
84
- ## 0.1.4 (2026-02-16)
85
-
86
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
87
-
88
- ## 0.1.3 (2026-02-16)
89
-
90
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
91
-
92
- ## 0.1.2 (2026-02-16)
93
-
94
- ### 🩹 Fixes
95
-
96
- - **release:** centralize manifestRootsToUpdate to update both source and dist ([2b8f832](https://github.com/DonaldMurillo/momentum-cms/commit/2b8f832))
97
- - **create-app:** fix Angular SSR, Analog builds, and CJS/ESM compatibility ([28d4d0a](https://github.com/DonaldMurillo/momentum-cms/commit/28d4d0a))
98
-
99
- ### ❤️ Thank You
100
-
101
- - Claude Opus 4.6
102
- - Donald Murillo @DonaldMurillo
103
-
104
- ## 0.1.1 (2026-02-16)
105
-
106
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
107
-
108
- ## 0.1.0 (2026-02-16)
109
-
110
- This was a version bump only for plugins-otel to align it with other projects, there were no code changes.
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024-present Momentum CMS Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.