@jaak.ai/stamps 2.1.0 → 2.2.0-dev.10

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 (57) hide show
  1. package/README.md +42 -9
  2. package/dist/cjs/{index-Ga0t6BMe.js → index-D6NBn_qu.js} +30 -5
  3. package/dist/cjs/index-D6NBn_qu.js.map +1 -0
  4. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
  5. package/dist/cjs/jaak-stamps.cjs.entry.js +17577 -12
  6. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  7. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  8. package/dist/cjs/loader.cjs.js +2 -2
  9. package/dist/collection/components/my-component/my-component.css +82 -2
  10. package/dist/collection/components/my-component/my-component.js +489 -10
  11. package/dist/collection/components/my-component/my-component.js.map +1 -1
  12. package/dist/collection/services/LicenseValidationService.js +111 -0
  13. package/dist/collection/services/LicenseValidationService.js.map +1 -0
  14. package/dist/collection/services/MetricsService.js +350 -0
  15. package/dist/collection/services/MetricsService.js.map +1 -0
  16. package/dist/collection/services/ServiceContainer.js +66 -1
  17. package/dist/collection/services/ServiceContainer.js.map +1 -1
  18. package/dist/collection/services/TracingService.js +494 -0
  19. package/dist/collection/services/TracingService.js.map +1 -0
  20. package/dist/collection/services/interfaces/ILicenseValidationService.js +2 -0
  21. package/dist/collection/services/interfaces/ILicenseValidationService.js.map +1 -0
  22. package/dist/collection/services/interfaces/IMetricsService.js +2 -0
  23. package/dist/collection/services/interfaces/IMetricsService.js.map +1 -0
  24. package/dist/collection/services/interfaces/ITracingService.js +2 -0
  25. package/dist/collection/services/interfaces/ITracingService.js.map +1 -0
  26. package/dist/components/index.js +28 -3
  27. package/dist/components/index.js.map +1 -1
  28. package/dist/components/jaak-stamps.js +17591 -11
  29. package/dist/components/jaak-stamps.js.map +1 -1
  30. package/dist/esm/{index-Drbzcuq-.js → index-BCfAsrzL.js} +30 -5
  31. package/dist/esm/index-BCfAsrzL.js.map +1 -0
  32. package/dist/esm/jaak-stamps-webcomponent.js +3 -3
  33. package/dist/esm/jaak-stamps.entry.js +17577 -12
  34. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  35. package/dist/esm/loader.js +3 -3
  36. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  37. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  38. package/dist/jaak-stamps-webcomponent/p-37281823.entry.js +7 -0
  39. package/dist/jaak-stamps-webcomponent/p-37281823.entry.js.map +1 -0
  40. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js +3 -0
  41. package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js.map +1 -0
  42. package/dist/types/components/my-component/my-component.d.ts +23 -0
  43. package/dist/types/components.d.ts +76 -0
  44. package/dist/types/services/LicenseValidationService.d.ts +59 -0
  45. package/dist/types/services/MetricsService.d.ts +74 -0
  46. package/dist/types/services/ServiceContainer.d.ts +14 -1
  47. package/dist/types/services/TracingService.d.ts +72 -0
  48. package/dist/types/services/interfaces/ILicenseValidationService.d.ts +4 -0
  49. package/dist/types/services/interfaces/IMetricsService.d.ts +70 -0
  50. package/dist/types/services/interfaces/ITracingService.d.ts +64 -0
  51. package/package.json +18 -2
  52. package/dist/cjs/index-Ga0t6BMe.js.map +0 -1
  53. package/dist/esm/index-Drbzcuq-.js.map +0 -1
  54. package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js +0 -3
  55. package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js.map +0 -1
  56. package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js +0 -2
  57. package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js.map +0 -1
@@ -0,0 +1,111 @@
1
+ /**
2
+ * License Validation Service
3
+ * Handles license validation for the jaak-stamps webcomponent
4
+ */
5
+ export class LicenseValidationService {
6
+ apiEndpoint = '/api/v1/license/validate';
7
+ baseUrl;
8
+ // Environment-specific base URLs
9
+ static ENVIRONMENT_URLS = {
10
+ dev: 'https://api.dev.jaak.ai',
11
+ qa: 'https://api.qa.jaak.ai',
12
+ sandbox: 'https://api.sandbox.jaak.ai',
13
+ prod: 'https://services.api.jaak.ai',
14
+ };
15
+ constructor(environment = 'prod') {
16
+ this.baseUrl = LicenseValidationService.ENVIRONMENT_URLS[environment];
17
+ }
18
+ /**
19
+ * Validate license with the API
20
+ * @param license License key to validate
21
+ * @param traceId Optional trace ID from W3C Trace Context
22
+ * @param appId Optional application ID
23
+ * @param metadata Optional additional metadata
24
+ * @returns Promise<LicenseValidationResponse>
25
+ */
26
+ async validateLicense(license, traceId, appId = 'jaak-stamps-web') {
27
+ // Generate traceId if not provided
28
+ const effectiveTraceId = traceId || this.generateTraceId();
29
+ // Build request body
30
+ const requestBody = {
31
+ license: traceId ? `L${effectiveTraceId}` : license,
32
+ origin: 'web',
33
+ app_id: appId,
34
+ product: 'kyc',
35
+ metadata: {
36
+ sdk_version: '2.1.0',
37
+ },
38
+ };
39
+ const headers = {
40
+ 'Content-Type': 'application/json',
41
+ };
42
+ // Add traceparent header with the effective traceId
43
+ headers['traceparent'] = `00-${effectiveTraceId}-${this.generateSpanId()}-01`;
44
+ try {
45
+ const response = await fetch(`${this.baseUrl}${this.apiEndpoint}`, {
46
+ method: 'POST',
47
+ headers,
48
+ body: JSON.stringify(requestBody),
49
+ });
50
+ if (!response.ok) {
51
+ const errorData = await response.json().catch(() => ({}));
52
+ throw {
53
+ error: errorData.error || 'license_validation_failed',
54
+ error_description: errorData.error_description || `HTTP ${response.status}: ${response.statusText}`,
55
+ status_code: response.status,
56
+ };
57
+ }
58
+ const responseData = await response.json();
59
+ // Add the traceId to the response
60
+ return {
61
+ ...responseData,
62
+ traceId: effectiveTraceId,
63
+ };
64
+ }
65
+ catch (error) {
66
+ if (error.error) {
67
+ throw error;
68
+ }
69
+ throw {
70
+ error: 'network_error',
71
+ error_description: 'Failed to connect to license validation service',
72
+ status_code: 0,
73
+ };
74
+ }
75
+ }
76
+ /**
77
+ * Generate a random trace ID for W3C Trace Context (32 hex characters)
78
+ */
79
+ generateTraceId() {
80
+ const array = new Uint8Array(16);
81
+ crypto.getRandomValues(array);
82
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
83
+ }
84
+ /**
85
+ * Generate a random span ID for W3C Trace Context (16 hex characters)
86
+ */
87
+ generateSpanId() {
88
+ const array = new Uint8Array(8);
89
+ crypto.getRandomValues(array);
90
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
91
+ }
92
+ /**
93
+ * Extract trace ID from license field
94
+ * License format: "L<32-hex-chars>"
95
+ * Example: "Ld79223833a2fd91971e4c14a33298904" -> "d79223833a2fd91971e4c14a33298904"
96
+ */
97
+ static extractTraceIdFromLicense(license) {
98
+ if (!license || typeof license !== 'string') {
99
+ return null;
100
+ }
101
+ // Remove "L" prefix if present
102
+ const traceId = license.startsWith('L') ? license.slice(1) : license;
103
+ // Validate format (32 hex characters)
104
+ if (traceId.length === 32 && /^[a-f0-9]{32}$/i.test(traceId)) {
105
+ return traceId.toLowerCase();
106
+ }
107
+ console.warn('[LicenseValidationService] Invalid license format for trace ID:', license);
108
+ return null;
109
+ }
110
+ }
111
+ //# sourceMappingURL=LicenseValidationService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LicenseValidationService.js","sourceRoot":"","sources":["../../src/services/LicenseValidationService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * License Validation Service\n * Handles license validation for the jaak-stamps webcomponent\n */\n\nexport type Environment = 'dev' | 'qa' | 'sandbox' | 'prod';\n\nexport interface LicenseValidationRequest {\n license: string;\n origin: string;\n app_id?: string;\n product?: string;\n product_version?: string;\n metadata?: {\n [key: string]: any;\n };\n}\n\nexport interface LicenseValidationResponse {\n access_token: string;\n token_type: string;\n expires_at: string;\n session_id: string;\n jti: string;\n step: number;\n license: string;\n traceId?: string; // Generated or provided traceId\n}\n\nexport interface LicenseValidationError {\n error: string;\n error_description?: string;\n status_code?: number;\n}\n\nexport class LicenseValidationService {\n private apiEndpoint: string = '/api/v1/license/validate';\n private baseUrl: string;\n\n // Environment-specific base URLs\n private static readonly ENVIRONMENT_URLS: Record<Environment, string> = {\n dev: 'https://api.dev.jaak.ai',\n qa: 'https://api.qa.jaak.ai',\n sandbox: 'https://api.sandbox.jaak.ai',\n prod: 'https://services.api.jaak.ai',\n };\n\n constructor(environment: Environment = 'prod') {\n this.baseUrl = LicenseValidationService.ENVIRONMENT_URLS[environment];\n }\n\n /**\n * Validate license with the API\n * @param license License key to validate\n * @param traceId Optional trace ID from W3C Trace Context\n * @param appId Optional application ID\n * @param metadata Optional additional metadata\n * @returns Promise<LicenseValidationResponse>\n */\n async validateLicense(\n license: string,\n traceId?: string,\n appId: string = 'jaak-stamps-web',\n ): Promise<LicenseValidationResponse> {\n // Generate traceId if not provided\n const effectiveTraceId = traceId || this.generateTraceId();\n\n // Build request body\n const requestBody: LicenseValidationRequest = {\n license: traceId ? `L${effectiveTraceId}` : license,\n origin: 'web',\n app_id: appId,\n product: 'kyc',\n metadata: {\n sdk_version: '2.1.0',\n },\n };\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n };\n\n // Add traceparent header with the effective traceId\n headers['traceparent'] = `00-${effectiveTraceId}-${this.generateSpanId()}-01`;\n\n try {\n const response = await fetch(`${this.baseUrl}${this.apiEndpoint}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw {\n error: errorData.error || 'license_validation_failed',\n error_description: errorData.error_description || `HTTP ${response.status}: ${response.statusText}`,\n status_code: response.status,\n } as LicenseValidationError;\n }\n\n const responseData = await response.json();\n // Add the traceId to the response\n return {\n ...responseData,\n traceId: effectiveTraceId,\n };\n } catch (error) {\n if ((error as any).error) {\n throw error;\n }\n\n throw {\n error: 'network_error',\n error_description: 'Failed to connect to license validation service',\n status_code: 0,\n } as LicenseValidationError;\n }\n }\n\n /**\n * Generate a random trace ID for W3C Trace Context (32 hex characters)\n */\n private generateTraceId(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Generate a random span ID for W3C Trace Context (16 hex characters)\n */\n private generateSpanId(): string {\n const array = new Uint8Array(8);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Extract trace ID from license field\n * License format: \"L<32-hex-chars>\"\n * Example: \"Ld79223833a2fd91971e4c14a33298904\" -> \"d79223833a2fd91971e4c14a33298904\"\n */\n static extractTraceIdFromLicense(license: string): string | null {\n if (!license || typeof license !== 'string') {\n return null;\n }\n\n // Remove \"L\" prefix if present\n const traceId = license.startsWith('L') ? license.slice(1) : license;\n\n // Validate format (32 hex characters)\n if (traceId.length === 32 && /^[a-f0-9]{32}$/i.test(traceId)) {\n return traceId.toLowerCase();\n }\n\n console.warn('[LicenseValidationService] Invalid license format for trace ID:', license);\n return null;\n }\n}\n"]}
@@ -0,0 +1,350 @@
1
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
2
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
3
+ export class MetricsService {
4
+ meterProvider = null;
5
+ meter = null;
6
+ collectorUrl;
7
+ serviceName;
8
+ serviceVersion;
9
+ debug;
10
+ customerId;
11
+ tenantId;
12
+ environment;
13
+ exportIntervalMillis;
14
+ metricReader = null;
15
+ // Business Metrics - Counters
16
+ captureCounter = null;
17
+ errorCounter = null;
18
+ modelLoadCounter = null;
19
+ userInteractionCounter = null;
20
+ // Business Metrics - Histograms (for latencies and sizes)
21
+ captureLatencyHistogram = null;
22
+ modelLoadLatencyHistogram = null;
23
+ detectionLatencyHistogram = null;
24
+ imageSizeHistogram = null;
25
+ // Business Metrics - Gauges (for current state)
26
+ activeSessionsGauge = null;
27
+ memoryUsageGauge = null;
28
+ // State tracking
29
+ activeSessions = 0;
30
+ gaugeValues = new Map();
31
+ constructor(config = {}) {
32
+ this.collectorUrl = config.collectorUrl || 'https://collector.jaak.ai/v1/metrics';
33
+ this.serviceName = config.serviceName || 'jaak-stamps-webcomponent';
34
+ this.serviceVersion = config.serviceVersion || '2.1.0';
35
+ this.debug = config.debug || false;
36
+ this.customerId = config.customerId;
37
+ this.tenantId = config.tenantId;
38
+ this.environment = config.environment || 'production';
39
+ this.exportIntervalMillis = config.exportIntervalMillis || 60000; // Export every 60 seconds
40
+ }
41
+ initialize() {
42
+ try {
43
+ // Create OTLP metrics exporter
44
+ const exporter = new OTLPMetricExporter({
45
+ url: this.collectorUrl,
46
+ headers: {
47
+ 'Content-Type': 'application/json',
48
+ },
49
+ });
50
+ // Create periodic exporting metric reader
51
+ this.metricReader = new PeriodicExportingMetricReader({
52
+ exporter: exporter,
53
+ exportIntervalMillis: this.exportIntervalMillis,
54
+ exportTimeoutMillis: 30000,
55
+ });
56
+ // Create meter provider
57
+ this.meterProvider = new MeterProvider({
58
+ readers: [this.metricReader],
59
+ });
60
+ // Get meter
61
+ this.meter = this.meterProvider.getMeter(this.serviceName, this.serviceVersion);
62
+ // Initialize business metrics
63
+ this.initializeMetrics();
64
+ if (this.debug) {
65
+ console.log('[MetricsService] Initialized successfully', {
66
+ collectorUrl: this.collectorUrl,
67
+ serviceName: this.serviceName,
68
+ serviceVersion: this.serviceVersion,
69
+ environment: this.environment,
70
+ customerId: this.customerId,
71
+ tenantId: this.tenantId,
72
+ exportIntervalMillis: this.exportIntervalMillis,
73
+ });
74
+ }
75
+ }
76
+ catch (error) {
77
+ console.error('[MetricsService] Failed to initialize:', error);
78
+ // Don't throw - metrics should fail silently in production
79
+ }
80
+ }
81
+ initializeMetrics() {
82
+ if (!this.meter)
83
+ return;
84
+ const baseAttributes = this.getBaseAttributes();
85
+ try {
86
+ // COUNTERS - Track events and totals
87
+ // Total captures performed
88
+ this.captureCounter = this.meter.createCounter('jaak.captures.total', {
89
+ description: 'Total number of document captures',
90
+ unit: '1',
91
+ });
92
+ // Total errors
93
+ this.errorCounter = this.meter.createCounter('jaak.errors.total', {
94
+ description: 'Total number of errors',
95
+ unit: '1',
96
+ });
97
+ // Model loads
98
+ this.modelLoadCounter = this.meter.createCounter('jaak.model.loads.total', {
99
+ description: 'Total number of model loads',
100
+ unit: '1',
101
+ });
102
+ // User interactions
103
+ this.userInteractionCounter = this.meter.createCounter('jaak.user.interactions.total', {
104
+ description: 'Total user interactions (clicks, etc.)',
105
+ unit: '1',
106
+ });
107
+ // HISTOGRAMS - Track distributions of latencies and sizes
108
+ // Capture latency (time from start to completion)
109
+ this.captureLatencyHistogram = this.meter.createHistogram('jaak.capture.duration', {
110
+ description: 'Duration of capture operations',
111
+ unit: 'ms',
112
+ });
113
+ // Model load latency
114
+ this.modelLoadLatencyHistogram = this.meter.createHistogram('jaak.model.load.duration', {
115
+ description: 'Duration of model loading',
116
+ unit: 'ms',
117
+ });
118
+ // Detection latency (per frame)
119
+ this.detectionLatencyHistogram = this.meter.createHistogram('jaak.detection.duration', {
120
+ description: 'Duration of detection inference',
121
+ unit: 'ms',
122
+ });
123
+ // Image sizes
124
+ this.imageSizeHistogram = this.meter.createHistogram('jaak.image.size', {
125
+ description: 'Size of captured images',
126
+ unit: 'bytes',
127
+ });
128
+ // GAUGES - Track current state values
129
+ // Active capture sessions
130
+ this.activeSessionsGauge = this.meter.createObservableGauge('jaak.sessions.active', {
131
+ description: 'Number of active capture sessions',
132
+ unit: '1',
133
+ });
134
+ this.activeSessionsGauge.addCallback((observableResult) => {
135
+ observableResult.observe(this.activeSessions, baseAttributes);
136
+ });
137
+ // Memory usage (if available)
138
+ if ('memory' in performance) {
139
+ this.memoryUsageGauge = this.meter.createObservableGauge('jaak.memory.usage', {
140
+ description: 'JavaScript heap memory usage',
141
+ unit: 'bytes',
142
+ });
143
+ this.memoryUsageGauge.addCallback((observableResult) => {
144
+ const memInfo = performance.memory;
145
+ if (memInfo) {
146
+ observableResult.observe(memInfo.usedJSHeapSize, baseAttributes);
147
+ }
148
+ });
149
+ }
150
+ if (this.debug) {
151
+ console.log('[MetricsService] Business metrics initialized');
152
+ }
153
+ }
154
+ catch (error) {
155
+ console.error('[MetricsService] Failed to initialize metrics:', error);
156
+ }
157
+ }
158
+ getBaseAttributes() {
159
+ const attributes = {
160
+ 'service.name': this.serviceName,
161
+ 'service.version': this.serviceVersion,
162
+ 'deployment.environment': this.environment,
163
+ };
164
+ if (this.customerId) {
165
+ attributes['customer.id'] = this.customerId;
166
+ }
167
+ if (this.tenantId) {
168
+ attributes['tenant.id'] = this.tenantId;
169
+ }
170
+ return attributes;
171
+ }
172
+ incrementCounter(name, value = 1, attributes) {
173
+ try {
174
+ const baseAttrs = this.getBaseAttributes();
175
+ const allAttrs = { ...baseAttrs, ...attributes };
176
+ // Use predefined counters or create ad-hoc
177
+ switch (name) {
178
+ case 'captures':
179
+ this.captureCounter?.add(value, allAttrs);
180
+ break;
181
+ case 'errors':
182
+ this.errorCounter?.add(value, allAttrs);
183
+ break;
184
+ case 'model_loads':
185
+ this.modelLoadCounter?.add(value, allAttrs);
186
+ break;
187
+ case 'user_interactions':
188
+ this.userInteractionCounter?.add(value, allAttrs);
189
+ break;
190
+ default:
191
+ // Create ad-hoc counter if needed
192
+ if (this.meter) {
193
+ const counter = this.meter.createCounter(`jaak.${name}.total`, {
194
+ description: `Total ${name}`,
195
+ unit: '1',
196
+ });
197
+ counter.add(value, allAttrs);
198
+ }
199
+ }
200
+ if (this.debug) {
201
+ console.log(`[MetricsService] Counter incremented: ${name} += ${value}`, attributes);
202
+ }
203
+ }
204
+ catch (error) {
205
+ console.error('[MetricsService] Failed to increment counter:', error);
206
+ }
207
+ }
208
+ recordHistogram(name, value, attributes) {
209
+ try {
210
+ const baseAttrs = this.getBaseAttributes();
211
+ const allAttrs = { ...baseAttrs, ...attributes };
212
+ // Use predefined histograms
213
+ switch (name) {
214
+ case 'capture_duration':
215
+ this.captureLatencyHistogram?.record(value, allAttrs);
216
+ break;
217
+ case 'model_load_duration':
218
+ this.modelLoadLatencyHistogram?.record(value, allAttrs);
219
+ break;
220
+ case 'detection_duration':
221
+ this.detectionLatencyHistogram?.record(value, allAttrs);
222
+ break;
223
+ case 'image_size':
224
+ this.imageSizeHistogram?.record(value, allAttrs);
225
+ break;
226
+ default:
227
+ // Create ad-hoc histogram if needed
228
+ if (this.meter) {
229
+ const histogram = this.meter.createHistogram(`jaak.${name}`, {
230
+ description: `Distribution of ${name}`,
231
+ unit: 'ms',
232
+ });
233
+ histogram.record(value, allAttrs);
234
+ }
235
+ }
236
+ if (this.debug) {
237
+ console.log(`[MetricsService] Histogram recorded: ${name} = ${value}`, attributes);
238
+ }
239
+ }
240
+ catch (error) {
241
+ console.error('[MetricsService] Failed to record histogram:', error);
242
+ }
243
+ }
244
+ setGauge(name, value, attributes) {
245
+ try {
246
+ // Store gauge value for observable callbacks
247
+ this.gaugeValues.set(name, value);
248
+ // Update known gauges
249
+ if (name === 'active_sessions') {
250
+ this.activeSessions = value;
251
+ }
252
+ if (this.debug) {
253
+ console.log(`[MetricsService] Gauge set: ${name} = ${value}`, attributes);
254
+ }
255
+ }
256
+ catch (error) {
257
+ console.error('[MetricsService] Failed to set gauge:', error);
258
+ }
259
+ }
260
+ async measureDuration(name, fn, attributes) {
261
+ const startTime = performance.now();
262
+ try {
263
+ const result = await fn();
264
+ const duration = performance.now() - startTime;
265
+ this.recordHistogram(name, duration, attributes);
266
+ return result;
267
+ }
268
+ catch (error) {
269
+ const duration = performance.now() - startTime;
270
+ this.recordHistogram(name, duration, { ...attributes, error: true });
271
+ throw error;
272
+ }
273
+ }
274
+ async flush() {
275
+ try {
276
+ if (this.metricReader) {
277
+ await this.metricReader.forceFlush();
278
+ }
279
+ if (this.debug) {
280
+ console.log('[MetricsService] Metrics flushed');
281
+ }
282
+ }
283
+ catch (error) {
284
+ console.error('[MetricsService] Failed to flush metrics:', error);
285
+ }
286
+ }
287
+ async cleanup() {
288
+ try {
289
+ if (this.metricReader) {
290
+ await this.metricReader.forceFlush();
291
+ await this.metricReader.shutdown();
292
+ }
293
+ if (this.meterProvider) {
294
+ await this.meterProvider.shutdown();
295
+ }
296
+ if (this.debug) {
297
+ console.log('[MetricsService] Cleaned up successfully');
298
+ }
299
+ }
300
+ catch (error) {
301
+ console.error('[MetricsService] Failed to cleanup:', error);
302
+ }
303
+ }
304
+ // Helper methods for common business metrics
305
+ /**
306
+ * Record a successful capture
307
+ */
308
+ recordCapture(side, mode, durationMs) {
309
+ this.incrementCounter('captures', 1, { side, mode });
310
+ this.recordHistogram('capture_duration', durationMs, { side, mode });
311
+ }
312
+ /**
313
+ * Record an error
314
+ */
315
+ recordError(errorType, operation) {
316
+ this.incrementCounter('errors', 1, { error_type: errorType, operation });
317
+ }
318
+ /**
319
+ * Record model load performance
320
+ */
321
+ recordModelLoad(modelType, durationMs, cached) {
322
+ this.incrementCounter('model_loads', 1, { model_type: modelType, cached });
323
+ this.recordHistogram('model_load_duration', durationMs, { model_type: modelType, cached });
324
+ }
325
+ /**
326
+ * Record detection performance
327
+ */
328
+ recordDetection(durationMs, detectionsFound) {
329
+ this.recordHistogram('detection_duration', durationMs, { detections_found: detectionsFound });
330
+ }
331
+ /**
332
+ * Record image size
333
+ */
334
+ recordImageSize(side, sizeBytes) {
335
+ this.recordHistogram('image_size', sizeBytes, { side });
336
+ }
337
+ /**
338
+ * Update active sessions count
339
+ */
340
+ updateActiveSessions(count) {
341
+ this.setGauge('active_sessions', count);
342
+ }
343
+ /**
344
+ * Record user interaction
345
+ */
346
+ recordUserInteraction(interactionType) {
347
+ this.incrementCounter('user_interactions', 1, { interaction_type: interactionType });
348
+ }
349
+ }
350
+ //# sourceMappingURL=MetricsService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricsService.js","sourceRoot":"","sources":["../../src/services/MetricsService.ts"],"names":[],"mappings":"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","sourcesContent":["import { IMetricsService } from './interfaces/IMetricsService';\nimport { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport type { Counter, Histogram, ObservableGauge, Meter } from '@opentelemetry/api';\n\nexport interface MetricsConfig {\n collectorUrl?: string;\n serviceName?: string;\n serviceVersion?: string;\n debug?: boolean;\n customerId?: string;\n tenantId?: string;\n environment?: string;\n exportIntervalMillis?: number;\n}\n\nexport class MetricsService implements IMetricsService {\n private meterProvider: MeterProvider | null = null;\n private meter: Meter | null = null;\n private readonly collectorUrl: string;\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly debug: boolean;\n private readonly customerId?: string;\n private readonly tenantId?: string;\n private readonly environment?: string;\n private readonly exportIntervalMillis: number;\n private metricReader: PeriodicExportingMetricReader | null = null;\n\n // Business Metrics - Counters\n private captureCounter: Counter | null = null;\n private errorCounter: Counter | null = null;\n private modelLoadCounter: Counter | null = null;\n private userInteractionCounter: Counter | null = null;\n\n // Business Metrics - Histograms (for latencies and sizes)\n private captureLatencyHistogram: Histogram | null = null;\n private modelLoadLatencyHistogram: Histogram | null = null;\n private detectionLatencyHistogram: Histogram | null = null;\n private imageSizeHistogram: Histogram | null = null;\n\n // Business Metrics - Gauges (for current state)\n private activeSessionsGauge: ObservableGauge | null = null;\n private memoryUsageGauge: ObservableGauge | null = null;\n\n // State tracking\n private activeSessions: number = 0;\n private gaugeValues: Map<string, number> = new Map();\n\n constructor(config: MetricsConfig = {}) {\n this.collectorUrl = config.collectorUrl || 'https://collector.jaak.ai/v1/metrics';\n this.serviceName = config.serviceName || 'jaak-stamps-webcomponent';\n this.serviceVersion = config.serviceVersion || '2.1.0';\n this.debug = config.debug || false;\n this.customerId = config.customerId;\n this.tenantId = config.tenantId;\n this.environment = config.environment || 'production';\n this.exportIntervalMillis = config.exportIntervalMillis || 60000; // Export every 60 seconds\n }\n\n initialize(): void {\n try {\n // Create OTLP metrics exporter\n const exporter = new OTLPMetricExporter({\n url: this.collectorUrl,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n // Create periodic exporting metric reader\n this.metricReader = new PeriodicExportingMetricReader({\n exporter: exporter,\n exportIntervalMillis: this.exportIntervalMillis,\n exportTimeoutMillis: 30000,\n });\n\n // Create meter provider\n this.meterProvider = new MeterProvider({\n readers: [this.metricReader],\n });\n\n // Get meter\n this.meter = this.meterProvider.getMeter(this.serviceName, this.serviceVersion);\n\n // Initialize business metrics\n this.initializeMetrics();\n\n if (this.debug) {\n console.log('[MetricsService] Initialized successfully', {\n collectorUrl: this.collectorUrl,\n serviceName: this.serviceName,\n serviceVersion: this.serviceVersion,\n environment: this.environment,\n customerId: this.customerId,\n tenantId: this.tenantId,\n exportIntervalMillis: this.exportIntervalMillis,\n });\n }\n } catch (error) {\n console.error('[MetricsService] Failed to initialize:', error);\n // Don't throw - metrics should fail silently in production\n }\n }\n\n private initializeMetrics(): void {\n if (!this.meter) return;\n\n const baseAttributes = this.getBaseAttributes();\n\n try {\n // COUNTERS - Track events and totals\n\n // Total captures performed\n this.captureCounter = this.meter.createCounter('jaak.captures.total', {\n description: 'Total number of document captures',\n unit: '1',\n });\n\n // Total errors\n this.errorCounter = this.meter.createCounter('jaak.errors.total', {\n description: 'Total number of errors',\n unit: '1',\n });\n\n // Model loads\n this.modelLoadCounter = this.meter.createCounter('jaak.model.loads.total', {\n description: 'Total number of model loads',\n unit: '1',\n });\n\n // User interactions\n this.userInteractionCounter = this.meter.createCounter('jaak.user.interactions.total', {\n description: 'Total user interactions (clicks, etc.)',\n unit: '1',\n });\n\n // HISTOGRAMS - Track distributions of latencies and sizes\n\n // Capture latency (time from start to completion)\n this.captureLatencyHistogram = this.meter.createHistogram('jaak.capture.duration', {\n description: 'Duration of capture operations',\n unit: 'ms',\n });\n\n // Model load latency\n this.modelLoadLatencyHistogram = this.meter.createHistogram('jaak.model.load.duration', {\n description: 'Duration of model loading',\n unit: 'ms',\n });\n\n // Detection latency (per frame)\n this.detectionLatencyHistogram = this.meter.createHistogram('jaak.detection.duration', {\n description: 'Duration of detection inference',\n unit: 'ms',\n });\n\n // Image sizes\n this.imageSizeHistogram = this.meter.createHistogram('jaak.image.size', {\n description: 'Size of captured images',\n unit: 'bytes',\n });\n\n // GAUGES - Track current state values\n\n // Active capture sessions\n this.activeSessionsGauge = this.meter.createObservableGauge('jaak.sessions.active', {\n description: 'Number of active capture sessions',\n unit: '1',\n });\n\n this.activeSessionsGauge.addCallback((observableResult) => {\n observableResult.observe(this.activeSessions, baseAttributes);\n });\n\n // Memory usage (if available)\n if ('memory' in performance) {\n this.memoryUsageGauge = this.meter.createObservableGauge('jaak.memory.usage', {\n description: 'JavaScript heap memory usage',\n unit: 'bytes',\n });\n\n this.memoryUsageGauge.addCallback((observableResult) => {\n const memInfo = (performance as any).memory;\n if (memInfo) {\n observableResult.observe(memInfo.usedJSHeapSize, baseAttributes);\n }\n });\n }\n\n if (this.debug) {\n console.log('[MetricsService] Business metrics initialized');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to initialize metrics:', error);\n }\n }\n\n private getBaseAttributes(): Record<string, any> {\n const attributes: Record<string, any> = {\n 'service.name': this.serviceName,\n 'service.version': this.serviceVersion,\n 'deployment.environment': this.environment,\n };\n\n if (this.customerId) {\n attributes['customer.id'] = this.customerId;\n }\n\n if (this.tenantId) {\n attributes['tenant.id'] = this.tenantId;\n }\n\n return attributes;\n }\n\n incrementCounter(name: string, value: number = 1, attributes?: Record<string, any>): void {\n try {\n const baseAttrs = this.getBaseAttributes();\n const allAttrs = { ...baseAttrs, ...attributes };\n\n // Use predefined counters or create ad-hoc\n switch (name) {\n case 'captures':\n this.captureCounter?.add(value, allAttrs);\n break;\n case 'errors':\n this.errorCounter?.add(value, allAttrs);\n break;\n case 'model_loads':\n this.modelLoadCounter?.add(value, allAttrs);\n break;\n case 'user_interactions':\n this.userInteractionCounter?.add(value, allAttrs);\n break;\n default:\n // Create ad-hoc counter if needed\n if (this.meter) {\n const counter = this.meter.createCounter(`jaak.${name}.total`, {\n description: `Total ${name}`,\n unit: '1',\n });\n counter.add(value, allAttrs);\n }\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Counter incremented: ${name} += ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to increment counter:', error);\n }\n }\n\n recordHistogram(name: string, value: number, attributes?: Record<string, any>): void {\n try {\n const baseAttrs = this.getBaseAttributes();\n const allAttrs = { ...baseAttrs, ...attributes };\n\n // Use predefined histograms\n switch (name) {\n case 'capture_duration':\n this.captureLatencyHistogram?.record(value, allAttrs);\n break;\n case 'model_load_duration':\n this.modelLoadLatencyHistogram?.record(value, allAttrs);\n break;\n case 'detection_duration':\n this.detectionLatencyHistogram?.record(value, allAttrs);\n break;\n case 'image_size':\n this.imageSizeHistogram?.record(value, allAttrs);\n break;\n default:\n // Create ad-hoc histogram if needed\n if (this.meter) {\n const histogram = this.meter.createHistogram(`jaak.${name}`, {\n description: `Distribution of ${name}`,\n unit: 'ms',\n });\n histogram.record(value, allAttrs);\n }\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Histogram recorded: ${name} = ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to record histogram:', error);\n }\n }\n\n setGauge(name: string, value: number, attributes?: Record<string, any>): void {\n try {\n // Store gauge value for observable callbacks\n this.gaugeValues.set(name, value);\n\n // Update known gauges\n if (name === 'active_sessions') {\n this.activeSessions = value;\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Gauge set: ${name} = ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to set gauge:', error);\n }\n }\n\n async measureDuration<T>(name: string, fn: () => T | Promise<T>, attributes?: Record<string, any>): Promise<T> {\n const startTime = performance.now();\n\n try {\n const result = await fn();\n const duration = performance.now() - startTime;\n\n this.recordHistogram(name, duration, attributes);\n\n return result;\n } catch (error) {\n const duration = performance.now() - startTime;\n this.recordHistogram(name, duration, { ...attributes, error: true });\n throw error;\n }\n }\n\n async flush(): Promise<void> {\n try {\n if (this.metricReader) {\n await this.metricReader.forceFlush();\n }\n\n if (this.debug) {\n console.log('[MetricsService] Metrics flushed');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to flush metrics:', error);\n }\n }\n\n async cleanup(): Promise<void> {\n try {\n if (this.metricReader) {\n await this.metricReader.forceFlush();\n await this.metricReader.shutdown();\n }\n\n if (this.meterProvider) {\n await this.meterProvider.shutdown();\n }\n\n if (this.debug) {\n console.log('[MetricsService] Cleaned up successfully');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to cleanup:', error);\n }\n }\n\n // Helper methods for common business metrics\n\n /**\n * Record a successful capture\n */\n recordCapture(side: 'front' | 'back', mode: 'auto' | 'manual', durationMs: number): void {\n this.incrementCounter('captures', 1, { side, mode });\n this.recordHistogram('capture_duration', durationMs, { side, mode });\n }\n\n /**\n * Record an error\n */\n recordError(errorType: string, operation: string): void {\n this.incrementCounter('errors', 1, { error_type: errorType, operation });\n }\n\n /**\n * Record model load performance\n */\n recordModelLoad(modelType: 'detection' | 'classification', durationMs: number, cached: boolean): void {\n this.incrementCounter('model_loads', 1, { model_type: modelType, cached });\n this.recordHistogram('model_load_duration', durationMs, { model_type: modelType, cached });\n }\n\n /**\n * Record detection performance\n */\n recordDetection(durationMs: number, detectionsFound: number): void {\n this.recordHistogram('detection_duration', durationMs, { detections_found: detectionsFound });\n }\n\n /**\n * Record image size\n */\n recordImageSize(side: 'front' | 'back', sizeBytes: number): void {\n this.recordHistogram('image_size', sizeBytes, { side });\n }\n\n /**\n * Update active sessions count\n */\n updateActiveSessions(count: number): void {\n this.setGauge('active_sessions', count);\n }\n\n /**\n * Record user interaction\n */\n recordUserInteraction(interactionType: string): void {\n this.incrementCounter('user_interactions', 1, { interaction_type: interactionType });\n }\n}\n"]}
@@ -2,6 +2,8 @@ import { EventBusService } from "./EventBusService";
2
2
  import { StateManagerService } from "./StateManagerService";
3
3
  import { CameraService } from "./CameraService";
4
4
  import { DetectionService } from "./DetectionService";
5
+ import { TracingService } from "./TracingService";
6
+ import { MetricsService } from "./MetricsService";
5
7
  export class ServiceContainer {
6
8
  services = new Map();
7
9
  constructor(config) {
@@ -13,11 +15,48 @@ export class ServiceContainer {
13
15
  const stateManager = new StateManagerService(eventBus);
14
16
  const cameraService = new CameraService(eventBus, config.preferredCamera);
15
17
  const detectionService = new DetectionService(config.debug, config.useDocumentClassification, config.useDocumentDetector);
18
+ // Initialize tracing service if enabled
19
+ let tracingService = null;
20
+ if (config.enableTelemetry !== false) {
21
+ tracingService = new TracingService({
22
+ collectorUrl: config.telemetryCollectorUrl || 'https://collector.jaak.ai/v1/traces',
23
+ serviceName: 'jaak-stamps-webcomponent',
24
+ serviceVersion: '2.1.0',
25
+ debug: config.debug,
26
+ customerId: config.customerId,
27
+ tenantId: config.tenantId,
28
+ environment: config.environment || 'production',
29
+ propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls || [],
30
+ enableAutoInstrumentation: true,
31
+ });
32
+ tracingService.initialize();
33
+ }
34
+ // Initialize metrics service if enabled
35
+ let metricsService = null;
36
+ if (config.enableMetrics !== false) {
37
+ metricsService = new MetricsService({
38
+ collectorUrl: config.metricsCollectorUrl || 'https://collector.jaak.ai/v1/metrics',
39
+ serviceName: 'jaak-stamps-webcomponent',
40
+ serviceVersion: '2.1.0',
41
+ debug: config.debug,
42
+ customerId: config.customerId,
43
+ tenantId: config.tenantId,
44
+ environment: config.environment || 'production',
45
+ exportIntervalMillis: config.metricsExportIntervalMillis || 60000,
46
+ });
47
+ metricsService.initialize();
48
+ }
16
49
  // Register services
17
50
  this.services.set('eventBus', eventBus);
18
51
  this.services.set('stateManager', stateManager);
19
52
  this.services.set('cameraService', cameraService);
20
53
  this.services.set('detectionService', detectionService);
54
+ if (tracingService) {
55
+ this.services.set('tracingService', tracingService);
56
+ }
57
+ if (metricsService) {
58
+ this.services.set('metricsService', metricsService);
59
+ }
21
60
  }
22
61
  get(serviceName) {
23
62
  const service = this.services.get(serviceName);
@@ -38,6 +77,22 @@ export class ServiceContainer {
38
77
  getDetectionService() {
39
78
  return this.get('detectionService');
40
79
  }
80
+ getTracingService() {
81
+ try {
82
+ return this.get('tracingService');
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ getMetricsService() {
89
+ try {
90
+ return this.get('metricsService');
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
41
96
  updateConfig(config) {
42
97
  // Reinitialize services that depend on configuration changes
43
98
  if (config.preferredCamera !== undefined ||
@@ -47,9 +102,19 @@ export class ServiceContainer {
47
102
  // For simplicity, this is omitted in this example
48
103
  }
49
104
  }
50
- cleanup() {
105
+ async cleanup() {
51
106
  // Cleanup services in reverse order
52
107
  this.getDetectionService().cleanup();
108
+ // Cleanup metrics service if present
109
+ const metricsService = this.getMetricsService();
110
+ if (metricsService) {
111
+ await metricsService.cleanup();
112
+ }
113
+ // Cleanup tracing service if present
114
+ const tracingService = this.getTracingService();
115
+ if (tracingService) {
116
+ await tracingService.cleanup();
117
+ }
53
118
  this.getEventBus().clear();
54
119
  this.services.clear();
55
120
  }