valtech-components 4.0.939 → 4.0.940

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,48 @@
1
+ import { Directive, Input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ import * as i1 from "../services/feature-control.service";
4
+ /**
5
+ * Structural directive to hide elements when a feature is disabled.
6
+ *
7
+ * Usage: <button *valFeatureGuard="'oauth_facebook'">Sign in with Facebook</button>
8
+ * If oauth_facebook is disabled, the button is not rendered.
9
+ */
10
+ export class FeatureGuardDirective {
11
+ set valFeatureGuard(featureKey) {
12
+ this.featureKey = featureKey;
13
+ this.updateVisibility();
14
+ }
15
+ constructor(templateRef, viewContainer, featureControl) {
16
+ this.templateRef = templateRef;
17
+ this.viewContainer = viewContainer;
18
+ this.featureControl = featureControl;
19
+ this.featureKey = '';
20
+ }
21
+ ngOnInit() {
22
+ this.updateVisibility();
23
+ }
24
+ updateVisibility() {
25
+ if (!this.featureKey) {
26
+ return;
27
+ }
28
+ const isEnabled = this.featureControl.isEnabled(this.featureKey);
29
+ if (isEnabled) {
30
+ this.viewContainer.createEmbeddedView(this.templateRef);
31
+ }
32
+ else {
33
+ this.viewContainer.clear();
34
+ }
35
+ }
36
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeatureGuardDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: i1.FeatureControlService }], target: i0.ɵɵFactoryTarget.Directive }); }
37
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: FeatureGuardDirective, isStandalone: true, selector: "[valFeatureGuard]", inputs: { valFeatureGuard: "valFeatureGuard" }, ngImport: i0 }); }
38
+ }
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeatureGuardDirective, decorators: [{
40
+ type: Directive,
41
+ args: [{
42
+ selector: '[valFeatureGuard]',
43
+ standalone: true,
44
+ }]
45
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: i1.FeatureControlService }], propDecorators: { valFeatureGuard: [{
46
+ type: Input
47
+ }] } });
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS1ndWFyZC5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2RpcmVjdGl2ZXMvZmVhdHVyZS1ndWFyZC5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQXlDLE1BQU0sZUFBZSxDQUFDOzs7QUFHeEY7Ozs7O0dBS0c7QUFLSCxNQUFNLE9BQU8scUJBQXFCO0lBQ2hDLElBQWEsZUFBZSxDQUFDLFVBQWtCO1FBQzdDLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDO1FBQzdCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzFCLENBQUM7SUFJRCxZQUNVLFdBQTZCLEVBQzdCLGFBQStCLEVBQy9CLGNBQXFDO1FBRnJDLGdCQUFXLEdBQVgsV0FBVyxDQUFrQjtRQUM3QixrQkFBYSxHQUFiLGFBQWEsQ0FBa0I7UUFDL0IsbUJBQWMsR0FBZCxjQUFjLENBQXVCO1FBTHZDLGVBQVUsR0FBVyxFQUFFLENBQUM7SUFNN0IsQ0FBQztJQUVKLFFBQVE7UUFDTixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRU8sZ0JBQWdCO1FBQ3RCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDckIsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDakUsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxhQUFhLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzFELENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixDQUFDO0lBQ0gsQ0FBQzsrR0E3QlUscUJBQXFCO21HQUFyQixxQkFBcUI7OzRGQUFyQixxQkFBcUI7a0JBSmpDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLG1CQUFtQjtvQkFDN0IsVUFBVSxFQUFFLElBQUk7aUJBQ2pCO21KQUVjLGVBQWU7c0JBQTNCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEaXJlY3RpdmUsIElucHV0LCBUZW1wbGF0ZVJlZiwgVmlld0NvbnRhaW5lclJlZiwgT25Jbml0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGZWF0dXJlQ29udHJvbFNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9mZWF0dXJlLWNvbnRyb2wuc2VydmljZSc7XG5cbi8qKlxuICogU3RydWN0dXJhbCBkaXJlY3RpdmUgdG8gaGlkZSBlbGVtZW50cyB3aGVuIGEgZmVhdHVyZSBpcyBkaXNhYmxlZC5cbiAqXG4gKiBVc2FnZTogPGJ1dHRvbiAqdmFsRmVhdHVyZUd1YXJkPVwiJ29hdXRoX2ZhY2Vib29rJ1wiPlNpZ24gaW4gd2l0aCBGYWNlYm9vazwvYnV0dG9uPlxuICogSWYgb2F1dGhfZmFjZWJvb2sgaXMgZGlzYWJsZWQsIHRoZSBidXR0b24gaXMgbm90IHJlbmRlcmVkLlxuICovXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbdmFsRmVhdHVyZUd1YXJkXScsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG59KVxuZXhwb3J0IGNsYXNzIEZlYXR1cmVHdWFyZERpcmVjdGl2ZSBpbXBsZW1lbnRzIE9uSW5pdCB7XG4gIEBJbnB1dCgpIHNldCB2YWxGZWF0dXJlR3VhcmQoZmVhdHVyZUtleTogc3RyaW5nKSB7XG4gICAgdGhpcy5mZWF0dXJlS2V5ID0gZmVhdHVyZUtleTtcbiAgICB0aGlzLnVwZGF0ZVZpc2liaWxpdHkoKTtcbiAgfVxuXG4gIHByaXZhdGUgZmVhdHVyZUtleTogc3RyaW5nID0gJyc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSB0ZW1wbGF0ZVJlZjogVGVtcGxhdGVSZWY8YW55PixcbiAgICBwcml2YXRlIHZpZXdDb250YWluZXI6IFZpZXdDb250YWluZXJSZWYsXG4gICAgcHJpdmF0ZSBmZWF0dXJlQ29udHJvbDogRmVhdHVyZUNvbnRyb2xTZXJ2aWNlXG4gICkge31cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLnVwZGF0ZVZpc2liaWxpdHkoKTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlVmlzaWJpbGl0eSgpIHtcbiAgICBpZiAoIXRoaXMuZmVhdHVyZUtleSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGlzRW5hYmxlZCA9IHRoaXMuZmVhdHVyZUNvbnRyb2wuaXNFbmFibGVkKHRoaXMuZmVhdHVyZUtleSk7XG4gICAgaWYgKGlzRW5hYmxlZCkge1xuICAgICAgdGhpcy52aWV3Q29udGFpbmVyLmNyZWF0ZUVtYmVkZGVkVmlldyh0aGlzLnRlbXBsYXRlUmVmKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy52aWV3Q29udGFpbmVyLmNsZWFyKCk7XG4gICAgfVxuICB9XG59XG4iXX0=
@@ -0,0 +1,117 @@
1
+ import { Injectable, signal, Optional } from '@angular/core';
2
+ import { firstValueFrom } from 'rxjs';
3
+ import * as i0 from "@angular/core";
4
+ import * as i1 from "@angular/common/http";
5
+ import * as i2 from "./firebase/analytics.service";
6
+ export class FeatureControlService {
7
+ constructor(http, analytics) {
8
+ this.http = http;
9
+ this.analytics = analytics;
10
+ this.features = signal(new Map());
11
+ this.isLoaded = signal(false);
12
+ this.previousState = new Map();
13
+ }
14
+ logFeatureEvent(key, enabled, previousEnabled) {
15
+ if (!this.analytics)
16
+ return;
17
+ try {
18
+ if (previousEnabled === null) {
19
+ // Carga inicial
20
+ this.analytics.logEvent('feature_loaded', {
21
+ feature_key: key,
22
+ feature_enabled: enabled,
23
+ });
24
+ }
25
+ else if (previousEnabled !== enabled) {
26
+ // Transición de estado
27
+ this.analytics.logEvent('feature_changed', {
28
+ feature_key: key,
29
+ previous_state: previousEnabled ? 'enabled' : 'disabled',
30
+ current_state: enabled ? 'enabled' : 'disabled',
31
+ });
32
+ }
33
+ }
34
+ catch (err) {
35
+ console.warn(`Failed to log feature event for ${key}:`, err);
36
+ }
37
+ }
38
+ /**
39
+ * Load user features from backend.
40
+ * Call this during app initialization (AppComponent ctor or effect).
41
+ * Logs feature state changes to Firebase Analytics.
42
+ */
43
+ async loadUserFeatures() {
44
+ try {
45
+ const response = await firstValueFrom(this.http.get('/v2/user/features'));
46
+ const map = new Map();
47
+ response.forEach(f => {
48
+ const previousEnabled = this.previousState.get(f.key) ?? null;
49
+ this.logFeatureEvent(f.key, f.enabled, previousEnabled);
50
+ this.previousState.set(f.key, f.enabled);
51
+ map.set(f.key, f.enabled);
52
+ });
53
+ this.features.set(map);
54
+ this.isLoaded.set(true);
55
+ }
56
+ catch (error) {
57
+ console.error('Failed to load feature control:', error);
58
+ // Fallback: assume all features enabled on error (optimistic)
59
+ this.isLoaded.set(true);
60
+ }
61
+ }
62
+ /**
63
+ * Check if a feature is enabled for the current user.
64
+ * Returns true if feature is not found (optimistic default).
65
+ * If rollout percentage is set, uses consistent hashing for canary rollouts.
66
+ */
67
+ isEnabled(featureKey) {
68
+ const featuresMap = this.features();
69
+ const feature = featuresMap.get(featureKey);
70
+ // Not in map: default to enabled (optimistic)
71
+ if (feature === undefined)
72
+ return true;
73
+ // Not enabled: return false
74
+ if (!feature)
75
+ return false;
76
+ // TODO: Implement rollout percentage check with hash
77
+ // For now, if enabled and no rollout, it's enabled for everyone
78
+ return true;
79
+ }
80
+ /**
81
+ * Get a computed signal for a feature.
82
+ * Use in templates: *ngIf="featureEnabled('oauth_facebook')()"
83
+ * Or in component: featureEnabled('payment_settings').
84
+ */
85
+ featureEnabled(featureKey) {
86
+ return () => this.isEnabled(featureKey);
87
+ }
88
+ /**
89
+ * Get all features for debugging (admin only).
90
+ */
91
+ getAllFeatures() {
92
+ return this.features();
93
+ }
94
+ /**
95
+ * Check if features have been loaded.
96
+ */
97
+ isReady() {
98
+ return this.isLoaded();
99
+ }
100
+ /**
101
+ * Watch for feature changes (for testing/demo purposes).
102
+ */
103
+ watchFeatures() {
104
+ return this.features;
105
+ }
106
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeatureControlService, deps: [{ token: i1.HttpClient }, { token: i2.AnalyticsService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
107
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeatureControlService, providedIn: 'root' }); }
108
+ }
109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeatureControlService, decorators: [{
110
+ type: Injectable,
111
+ args: [{
112
+ providedIn: 'root',
113
+ }]
114
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.AnalyticsService, decorators: [{
115
+ type: Optional
116
+ }] }] });
117
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS1jb250cm9sLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL3NlcnZpY2VzL2ZlYXR1cmUtY29udHJvbC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU3RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sTUFBTSxDQUFDOzs7O0FBYXRDLE1BQU0sT0FBTyxxQkFBcUI7SUFLaEMsWUFDVSxJQUFnQixFQUNKLFNBQTRCO1FBRHhDLFNBQUksR0FBSixJQUFJLENBQVk7UUFDSixjQUFTLEdBQVQsU0FBUyxDQUFtQjtRQU4xQyxhQUFRLEdBQUcsTUFBTSxDQUF1QixJQUFJLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDbkQsYUFBUSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6QixrQkFBYSxHQUFHLElBQUksR0FBRyxFQUFtQixDQUFDO0lBS2hELENBQUM7SUFFSSxlQUFlLENBQUMsR0FBVyxFQUFFLE9BQWdCLEVBQUUsZUFBK0I7UUFDcEYsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTO1lBQUUsT0FBTztRQUU1QixJQUFJLENBQUM7WUFDSCxJQUFJLGVBQWUsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDN0IsZ0JBQWdCO2dCQUNoQixJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRTtvQkFDeEMsV0FBVyxFQUFFLEdBQUc7b0JBQ2hCLGVBQWUsRUFBRSxPQUFPO2lCQUN6QixDQUFDLENBQUM7WUFDTCxDQUFDO2lCQUFNLElBQUksZUFBZSxLQUFLLE9BQU8sRUFBRSxDQUFDO2dCQUN2Qyx1QkFBdUI7Z0JBQ3ZCLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLGlCQUFpQixFQUFFO29CQUN6QyxXQUFXLEVBQUUsR0FBRztv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QUUsTUFBTTtpQkFDbkI7OzBCQVFJLFFBQVEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBzaWduYWwsIE9wdGlvbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBIdHRwQ2xpZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgZmlyc3RWYWx1ZUZyb20gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEFuYWx5dGljc1NlcnZpY2UgfSBmcm9tICcuL2ZpcmViYXNlL2FuYWx5dGljcy5zZXJ2aWNlJztcblxuZXhwb3J0IGludGVyZmFjZSBVc2VyRmVhdHVyZSB7XG4gIGtleTogc3RyaW5nO1xuICBlbmFibGVkOiBib29sZWFuO1xuICByb2xsb3V0UGVyY2VudGFnZT86IG51bWJlcjsgLy8gMC0xMDA6IHBlcmNlbnRhZ2Ugb2YgdXNlcnMgd2hvIGdldCB0aGlzIGZlYXR1cmVcbiAgcm9sbG91dEhhc2g/OiBzdHJpbmc7IC8vIEhhc2ggdXNlZCBmb3IgY29uc2lzdGVudCByb2xsb3V0IChlLmcuLCB1c2VyIElEIGhhc2gpXG59XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnLFxufSlcbmV4cG9ydCBjbGFzcyBGZWF0dXJlQ29udHJvbFNlcnZpY2Uge1xuICBwcml2YXRlIGZlYXR1cmVzID0gc2lnbmFsPE1hcDxzdHJpbmcsIGJvb2xlYW4+PihuZXcgTWFwKCkpO1xuICBwcml2YXRlIGlzTG9hZGVkID0gc2lnbmFsKGZhbHNlKTtcbiAgcHJpdmF0ZSBwcmV2aW91c1N0YXRlID0gbmV3IE1hcDxzdHJpbmcsIGJvb2xlYW4+KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50LFxuICAgIEBPcHRpb25hbCgpIHByaXZhdGUgYW5hbHl0aWNzPzogQW5hbHl0aWNzU2VydmljZVxuICApIHt9XG5cbiAgcHJpdmF0ZSBsb2dGZWF0dXJlRXZlbnQoa2V5OiBzdHJpbmcsIGVuYWJsZWQ6IGJvb2xlYW4sIHByZXZpb3VzRW5hYmxlZDogYm9vbGVhbiB8IG51bGwpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYW5hbHl0aWNzKSByZXR1cm47XG5cbiAgICB0cnkge1xuICAgICAgaWYgKHByZXZpb3VzRW5hYmxlZCA9PT0gbnVsbCkge1xuICAgICAgICAvLyBDYXJnYSBpbmljaWFsXG4gICAgICAgIHRoaXMuYW5hbHl0aWNzLmxvZ0V2ZW50KCdmZWF0dXJlX2xvYWRlZCcsIHtcbiAgICAgICAgICBmZWF0dXJlX2tleToga2V5LFxuICAgICAgICAgIGZlYXR1cmVfZW5hYmxlZDogZW5hYmxlZCxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKHByZXZpb3VzRW5hYmxlZCAhPT0gZW5hYmxlZCkge1xuICAgICAgICAvLyBUcmFuc2ljacOzbiBkZSBlc3RhZG9cbiAgICAgICAgdGhpcy5hbmFseXRpY3MubG9nRXZlbnQoJ2ZlYXR1cmVfY2hhbmdlZCcsIHtcbiAgICAgICAgICBmZWF0dXJlX2tleToga2V5LFxuICAgICAgICAgIHByZXZpb3VzX3N0YXRlOiBwcmV2aW91c0VuYWJsZWQgPyAnZW5hYmxlZCcgOiAnZGlzYWJsZWQnLFxuICAgICAgICAgIGN1cnJlbnRfc3RhdGU6IGVuYWJsZWQgPyAnZW5hYmxlZCcgOiAnZGlzYWJsZWQnLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUud2FybihgRmFpbGVkIHRvIGxvZyBmZWF0dXJlIGV2ZW50IGZvciAke2tleX06YCwgZXJyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTG9hZCB1c2VyIGZlYXR1cmVzIGZyb20gYmFja2VuZC5cbiAgICogQ2FsbCB0aGlzIGR1cmluZyBhcHAgaW5pdGlhbGl6YXRpb24gKEFwcENvbXBvbmVudCBjdG9yIG9yIGVmZmVjdCkuXG4gICAqIExvZ3MgZmVhdHVyZSBzdGF0ZSBjaGFuZ2VzIHRvIEZpcmViYXNlIEFuYWx5dGljcy5cbiAgICovXG4gIGFzeW5jIGxvYWRVc2VyRmVhdHVyZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20odGhpcy5odHRwLmdldDxVc2VyRmVhdHVyZVtdPignL3YyL3VzZXIvZmVhdHVyZXMnKSk7XG4gICAgICBjb25zdCBtYXAgPSBuZXcgTWFwPHN0cmluZywgYm9vbGVhbj4oKTtcblxuICAgICAgcmVzcG9uc2UuZm9yRWFjaChmID0+IHtcbiAgICAgICAgY29uc3QgcHJldmlvdXNFbmFibGVkID0gdGhpcy5wcmV2aW91c1N0YXRlLmdldChmLmtleSkgPz8gbnVsbDtcbiAgICAgICAgdGhpcy5sb2dGZWF0dXJlRXZlbnQoZi5rZXksIGYuZW5hYmxlZCwgcHJldmlvdXNFbmFibGVkKTtcbiAgICAgICAgdGhpcy5wcmV2aW91c1N0YXRlLnNldChmLmtleSwgZi5lbmFibGVkKTtcbiAgICAgICAgbWFwLnNldChmLmtleSwgZi5lbmFibGVkKTtcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLmZlYXR1cmVzLnNldChtYXApO1xuICAgICAgdGhpcy5pc0xvYWRlZC5zZXQodHJ1ZSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIGZlYXR1cmUgY29udHJvbDonLCBlcnJvcik7XG4gICAgICAvLyBGYWxsYmFjazogYXNzdW1lIGFsbCBmZWF0dXJlcyBlbmFibGVkIG9uIGVycm9yIChvcHRpbWlzdGljKVxuICAgICAgdGhpcy5pc0xvYWRlZC5zZXQodHJ1ZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGEgZmVhdHVyZSBpcyBlbmFibGVkIGZvciB0aGUgY3VycmVudCB1c2VyLlxuICAgKiBSZXR1cm5zIHRydWUgaWYgZmVhdHVyZSBpcyBub3QgZm91bmQgKG9wdGltaXN0aWMgZGVmYXVsdCkuXG4gICAqIElmIHJvbGxvdXQgcGVyY2VudGFnZSBpcyBzZXQsIHVzZXMgY29uc2lzdGVudCBoYXNoaW5nIGZvciBjYW5hcnkgcm9sbG91dHMuXG4gICAqL1xuICBpc0VuYWJsZWQoZmVhdHVyZUtleTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZmVhdHVyZXNNYXAgPSB0aGlzLmZlYXR1cmVzKCk7XG4gICAgY29uc3QgZmVhdHVyZSA9IGZlYXR1cmVzTWFwLmdldChmZWF0dXJlS2V5KTtcblxuICAgIC8vIE5vdCBpbiBtYXA6IGRlZmF1bHQgdG8gZW5hYmxlZCAob3B0aW1pc3RpYylcbiAgICBpZiAoZmVhdHVyZSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdHJ1ZTtcblxuICAgIC8vIE5vdCBlbmFibGVkOiByZXR1cm4gZmFsc2VcbiAgICBpZiAoIWZlYXR1cmUpIHJldHVybiBmYWxzZTtcblxuICAgIC8vIFRPRE86IEltcGxlbWVudCByb2xsb3V0IHBlcmNlbnRhZ2UgY2hlY2sgd2l0aCBoYXNoXG4gICAgLy8gRm9yIG5vdywgaWYgZW5hYmxlZCBhbmQgbm8gcm9sbG91dCwgaXQncyBlbmFibGVkIGZvciBldmVyeW9uZVxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIGNvbXB1dGVkIHNpZ25hbCBmb3IgYSBmZWF0dXJlLlxuICAgKiBVc2UgaW4gdGVtcGxhdGVzOiAqbmdJZj1cImZlYXR1cmVFbmFibGVkKCdvYXV0aF9mYWNlYm9vaycpKClcIlxuICAgKiBPciBpbiBjb21wb25lbnQ6IGZlYXR1cmVFbmFibGVkKCdwYXltZW50X3NldHRpbmdzJykuXG4gICAqL1xuICBmZWF0dXJlRW5hYmxlZChmZWF0dXJlS2V5OiBzdHJpbmcpIHtcbiAgICByZXR1cm4gKCkgPT4gdGhpcy5pc0VuYWJsZWQoZmVhdHVyZUtleSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBmZWF0dXJlcyBmb3IgZGVidWdnaW5nIChhZG1pbiBvbmx5KS5cbiAgICovXG4gIGdldEFsbEZlYXR1cmVzKCk6IE1hcDxzdHJpbmcsIGJvb2xlYW4+IHtcbiAgICByZXR1cm4gdGhpcy5mZWF0dXJlcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGZlYXR1cmVzIGhhdmUgYmVlbiBsb2FkZWQuXG4gICAqL1xuICBpc1JlYWR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzTG9hZGVkKCk7XG4gIH1cblxuICAvKipcbiAgICogV2F0Y2ggZm9yIGZlYXR1cmUgY2hhbmdlcyAoZm9yIHRlc3RpbmcvZGVtbyBwdXJwb3NlcykuXG4gICAqL1xuICB3YXRjaEZlYXR1cmVzKCkge1xuICAgIHJldHVybiB0aGlzLmZlYXR1cmVzO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Contrato de marca de un QR, compartido entre backend y frontend (ADR-078).
3
+ *
4
+ * Espejo TypeScript de `backend/go/services/pkg/qrbrand/qrbrand.go`. Los tests
5
+ * de contraste de este archivo usan LOS MISMOS casos que el lado Go
6
+ * (`qrbrand_test.go`) — si un lado cambia el número esperado sin el otro,
7
+ * dejan de coincidir.
8
+ *
9
+ * No dibuja nada: valida que una combinación de colores sea escaneable y
10
+ * decide cuánta corrección de errores necesita un QR que lleva logo encima.
11
+ * El renderizado real sigue siendo QrGeneratorService (cliente) y qrpng.go
12
+ * (backend, correo) — cada uno dibuja a su manera; lo que comparten es esta
13
+ * validación.
14
+ */
15
+ /** Sin marca configurada: tinta sobre blanco. Nunca se persiste. */
16
+ export function defaultQrBrand(orgId, appId) {
17
+ return { orgId, appId, ink: '#111111', bg: '#FFFFFF', dotStyle: 'square' };
18
+ }
19
+ const HEX_RE = /^#([0-9a-fA-F]{6})$/;
20
+ /** El mínimo WCAG para texto normal (AA) — ver qrbrand.go para el porqué de reusarlo acá. */
21
+ const MIN_CONTRAST_RATIO = 4.5;
22
+ export class QrBrandValidationError extends Error {
23
+ constructor(field, message) {
24
+ super(`${field}: ${message}`);
25
+ this.field = field;
26
+ this.name = 'QrBrandValidationError';
27
+ }
28
+ }
29
+ function hexToRgb(hex) {
30
+ const m = HEX_RE.exec(hex);
31
+ if (!m)
32
+ return [0, 0, 0];
33
+ const v = parseInt(m[1], 16);
34
+ return [((v >> 16) & 0xff) / 255, ((v >> 8) & 0xff) / 255, (v & 0xff) / 255];
35
+ }
36
+ function srgbToLinear(c) {
37
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
38
+ }
39
+ function relativeLuminance(hex) {
40
+ const [r, g, b] = hexToRgb(hex);
41
+ const [rl, gl, bl] = [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];
42
+ return 0.2126 * rl + 0.7152 * gl + 0.0722 * bl;
43
+ }
44
+ /** Contraste WCAG entre dos colores hex (#RRGGBB). Fórmula estándar (L1+0.05)/(L2+0.05). */
45
+ export function qrContrastRatio(hexA, hexB) {
46
+ let la = relativeLuminance(hexA);
47
+ let lb = relativeLuminance(hexB);
48
+ if (la < lb)
49
+ [la, lb] = [lb, la];
50
+ return (la + 0.05) / (lb + 0.05);
51
+ }
52
+ /**
53
+ * Rechaza una combinación de colores que un lector real no va a poder leer.
54
+ * Escaneabilidad sobre estética: esta función no negocia (ADR-078 regla 1).
55
+ * Lanza QrBrandValidationError; no retorna un booleano — el caller casi
56
+ * siempre necesita el mensaje para mostrarlo en el formulario.
57
+ */
58
+ export function validateQrBrand(b) {
59
+ if (!HEX_RE.test(b.ink)) {
60
+ throw new QrBrandValidationError('ink', 'debe ser un color hex de 6 dígitos, ej. #111111');
61
+ }
62
+ if (!HEX_RE.test(b.bg)) {
63
+ throw new QrBrandValidationError('bg', 'debe ser un color hex de 6 dígitos, ej. #FFFFFF');
64
+ }
65
+ if (b.dotStyle && !['square', 'rounded', 'dots'].includes(b.dotStyle)) {
66
+ throw new QrBrandValidationError('dotStyle', 'debe ser square, rounded o dots');
67
+ }
68
+ const ratio = qrContrastRatio(b.ink, b.bg);
69
+ if (ratio < MIN_CONTRAST_RATIO) {
70
+ throw new QrBrandValidationError('ink/bg', `contraste ${ratio.toFixed(2)}:1 por debajo del mínimo ${MIN_CONTRAST_RATIO.toFixed(1)}:1 — un lector real no va a poder escanearlo`);
71
+ }
72
+ if (b.logo && (b.logo.clearArea ?? 0) > 0.3) {
73
+ throw new QrBrandValidationError('logo.clearArea', 'un logo que tapa más del 30% del QR deja de ser legible aunque suba la corrección de errores');
74
+ }
75
+ }
76
+ /**
77
+ * El logo obliga a subir la corrección de errores, no se le pregunta al
78
+ * usuario (ADR-078 regla 2). Sin logo, M alcanza. Con logo, sube a Q; con
79
+ * logo grande (>15% del área), sube al máximo H.
80
+ */
81
+ export function qrErrorCorrectionFor(b) {
82
+ const clearArea = b.logo?.clearArea ?? 0;
83
+ if (!b.logo || clearArea <= 0)
84
+ return 'M';
85
+ if (clearArea > 0.15)
86
+ return 'H';
87
+ return 'Q';
88
+ }
89
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXJicmFuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvcXItZ2VuZXJhdG9yL3FyYnJhbmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRztBQXFCSCxvRUFBb0U7QUFDcEUsTUFBTSxVQUFVLGNBQWMsQ0FBQyxLQUFhLEVBQUUsS0FBYTtJQUN6RCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQzdFLENBQUM7QUFFRCxNQUFNLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQztBQUVyQyw2RkFBNkY7QUFDN0YsTUFBTSxrQkFBa0IsR0FBRyxHQUFHLENBQUM7QUFFL0IsTUFBTSxPQUFPLHNCQUF1QixTQUFRLEtBQUs7SUFDL0MsWUFDa0IsS0FBYSxFQUM3QixPQUFlO1FBRWYsS0FBSyxDQUFDLEdBQUcsS0FBSyxLQUFLLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFIZCxVQUFLLEdBQUwsS0FBSyxDQUFRO1FBSTdCLElBQUksQ0FBQyxJQUFJLEdBQUcsd0JBQXdCLENBQUM7SUFDdkMsQ0FBQztDQUNGO0FBRUQsU0FBUyxRQUFRLENBQUMsR0FBVztJQUMzQixNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLElBQUksQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDekIsTUFBTSxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM3QixPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxHQUFHLEVBQUUs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LENBQzlCLGdCQUFnQixFQUNoQiw4RkFBOEYsQ0FDL0YsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxvQkFBb0IsQ0FBQyxDQUFVO0lBQzdDLE1BQU0sU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsU0FBUyxJQUFJLENBQUMsQ0FBQztJQUN6QyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxTQUFTLElBQUksQ0FBQztRQUFFLE9BQU8sR0FBRyxDQUFDO0lBQzFDLElBQUksU0FBUyxHQUFHLElBQUk7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUNqQyxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbnRyYXRvIGRlIG1hcmNhIGRlIHVuIFFSLCBjb21wYXJ0aWRvIGVudHJlIGJhY2tlbmQgeSBmcm9udGVuZCAoQURSLTA3OCkuXG4gKlxuICogRXNwZWpvIFR5cGVTY3JpcHQgZGUgYGJhY2tlbmQvZ28vc2VydmljZXMvcGtnL3FyYnJhbmQvcXJicmFuZC5nb2AuIExvcyB0ZXN0c1xuICogZGUgY29udHJhc3RlIGRlIGVzdGUgYXJjaGl2byB1c2FuIExPUyBNSVNNT1MgY2Fzb3MgcXVlIGVsIGxhZG8gR29cbiAqIChgcXJicmFuZF90ZXN0LmdvYCkg4oCUIHNpIHVuIGxhZG8gY2FtYmlhIGVsIG7Dum1lcm8gZXNwZXJhZG8gc2luIGVsIG90cm8sXG4gKiBkZWphbiBkZSBjb2luY2lkaXIuXG4gKlxuICogTm8gZGlidWphIG5hZGE6IHZhbGlkYSBxdWUgdW5hIGNvbWJpbmFjacOzbiBkZSBjb2xvcmVzIHNlYSBlc2NhbmVhYmxlIHlcbiAqIGRlY2lkZSBjdcOhbnRhIGNvcnJlY2Npw7NuIGRlIGVycm9yZXMgbmVjZXNpdGEgdW4gUVIgcXVlIGxsZXZhIGxvZ28gZW5jaW1hLlxuICogRWwgcmVuZGVyaXphZG8gcmVhbCBzaWd1ZSBzaWVuZG8gUXJHZW5lcmF0b3JTZXJ2aWNlIChjbGllbnRlKSB5IHFycG5nLmdvXG4gKiAoYmFja2VuZCwgY29ycmVvKSDigJQgY2FkYSB1bm8gZGlidWphIGEgc3UgbWFuZXJhOyBsbyBxdWUgY29tcGFydGVuIGVzIGVzdGFcbiAqIHZhbGlkYWNpw7NuLlxuICovXG5cbmV4cG9ydCB0eXBlIFFyQnJhbmREb3RTdHlsZSA9ICdzcXVhcmUnIHwgJ3JvdW5kZWQnIHwgJ2RvdHMnO1xuXG5leHBvcnQgdHlwZSBRckJyYW5kRXJyb3JDb3JyZWN0aW9uID0gJ0wnIHwgJ00nIHwgJ1EnIHwgJ0gnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFFyQnJhbmRMb2dvIHtcbiAgYXNzZXRJZDogc3RyaW5nO1xuICAvKiogRnJhY2Npw7NuIGRlbCBRUiAoMOKAkzEpIHF1ZSBlbCBsb2dvIHRhcGEgZW4gZWwgY2VudHJvLiAqL1xuICBjbGVhckFyZWE/OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUXJCcmFuZCB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIGFwcElkOiBzdHJpbmc7XG4gIGluazogc3RyaW5nO1xuICBiZzogc3RyaW5nO1xuICBkb3RTdHlsZT86IFFyQnJhbmREb3RTdHlsZTtcbiAgbG9nbz86IFFyQnJhbmRMb2dvO1xufVxuXG4vKiogU2luIG1hcmNhIGNvbmZpZ3VyYWRhOiB0aW50YSBzb2JyZSBibGFuY28uIE51bmNhIHNlIHBlcnNpc3RlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRRckJyYW5kKG9yZ0lkOiBzdHJpbmcsIGFwcElkOiBzdHJpbmcpOiBRckJyYW5kIHtcbiAgcmV0dXJuIHsgb3JnSWQsIGFwcElkLCBpbms6ICcjMTExMTExJywgYmc6ICcjRkZGRkZGJywgZG90U3R5bGU6ICdzcXVhcmUnIH07XG59XG5cbmNvbnN0IEhFWF9SRSA9IC9eIyhbMC05YS1mQS1GXXs2fSkkLztcblxuLyoqIEVsIG3DrW5pbW8gV0NBRyBwYXJhIHRleHRvIG5vcm1hbCAoQUEpIOKAlCB2ZXIgcXJicmFuZC5nbyBwYXJhIGVsIHBvcnF1w6kgZGUgcmV1c2FybG8gYWPDoS4gKi9cbmNvbnN0IE1JTl9DT05UUkFTVF9SQVRJTyA9IDQuNTtcblxuZXhwb3J0IGNsYXNzIFFyQnJhbmRWYWxpZGF0aW9uRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyByZWFkb25seSBmaWVsZDogc3RyaW5nLFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgKSB7XG4gICAgc3VwZXIoYCR7ZmllbGR9OiAke21lc3NhZ2V9YCk7XG4gICAgdGhpcy5uYW1lID0gJ1FyQnJhbmRWYWxpZGF0aW9uRXJyb3InO1xuICB9XG59XG5cbmZ1bmN0aW9uIGhleFRvUmdiKGhleDogc3RyaW5nKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgbSA9IEhFWF9SRS5leGVjKGhleCk7XG4gIGlmICghbSkgcmV0dXJuIFswLCAwLCAwXTtcbiAgY29uc3QgdiA9IHBhcnNlSW50KG1bMV0sIDE2KTtcbiAgcmV0dXJuIFsoKHYgPj4gMTYpICYgMHhmZikgLyAyNTUsICgodiA+PiA4KSAmIDB4ZmYpIC8gMjU1LCAodiAmIDB4ZmYpIC8gMjU1XTtcbn1cblxuZnVuY3Rpb24gc3JnYlRvTGluZWFyKGM6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBjIDw9IDAuMDM5MjggPyBjIC8gMTIuOTIgOiBNYXRoLnBvdygoYyArIDAuMDU1KSAvIDEuMDU1LCAyLjQpO1xufVxuXG5mdW5jdGlvbiByZWxhdGl2ZUx1bWluYW5jZShoZXg6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IFtyLCBnLCBiXSA9IGhleFRvUmdiKGhleCk7XG4gIGNvbnN0IFtybCwgZ2wsIGJsXSA9IFtzcmdiVG9MaW5lYXIociksIHNyZ2JUb0xpbmVhcihnKSwgc3JnYlRvTGluZWFyKGIpXTtcbiAgcmV0dXJuIDAuMjEyNiAqIHJsICsgMC43MTUyICogZ2wgKyAwLjA3MjIgKiBibDtcbn1cblxuLyoqIENvbnRyYXN0ZSBXQ0FHIGVudHJlIGRvcyBjb2xvcmVzIGhleCAoI1JSR0dCQikuIEbDs3JtdWxhIGVzdMOhbmRhciAoTDErMC4wNSkvKEwyKzAuMDUpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHFyQ29udHJhc3RSYXRpbyhoZXhBOiBzdHJpbmcsIGhleEI6IHN0cmluZyk6IG51bWJlciB7XG4gIGxldCBsYSA9IHJlbGF0aXZlTHVtaW5hbmNlKGhleEEpO1xuICBsZXQgbGIgPSByZWxhdGl2ZUx1bWluYW5jZShoZXhCKTtcbiAgaWYgKGxhIDwgbGIpIFtsYSwgbGJdID0gW2xiLCBsYV07XG4gIHJldHVybiAobGEgKyAwLjA1KSAvIChsYiArIDAuMDUpO1xufVxuXG4vKipcbiAqIFJlY2hhemEgdW5hIGNvbWJpbmFjacOzbiBkZSBjb2xvcmVzIHF1ZSB1biBsZWN0b3IgcmVhbCBubyB2YSBhIHBvZGVyIGxlZXIuXG4gKiBFc2NhbmVhYmlsaWRhZCBzb2JyZSBlc3TDqXRpY2E6IGVzdGEgZnVuY2nDs24gbm8gbmVnb2NpYSAoQURSLTA3OCByZWdsYSAxKS5cbiAqIExhbnphIFFyQnJhbmRWYWxpZGF0aW9uRXJyb3I7IG5vIHJldG9ybmEgdW4gYm9vbGVhbm8g4oCUIGVsIGNhbGxlciBjYXNpXG4gKiBzaWVtcHJlIG5lY2VzaXRhIGVsIG1lbnNhamUgcGFyYSBtb3N0cmFybG8gZW4gZWwgZm9ybXVsYXJpby5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlUXJCcmFuZChiOiBRckJyYW5kKTogdm9pZCB7XG4gIGlmICghSEVYX1JFLnRlc3QoYi5pbmspKSB7XG4gICAgdGhyb3cgbmV3IFFyQnJhbmRWYWxpZGF0aW9uRXJyb3IoJ2luaycsICdkZWJlIHNlciB1biBjb2xvciBoZXggZGUgNiBkw61naXRvcywgZWouICMxMTExMTEnKTtcbiAgfVxuICBpZiAoIUhFWF9SRS50ZXN0KGIuYmcpKSB7XG4gICAgdGhyb3cgbmV3IFFyQnJhbmRWYWxpZGF0aW9uRXJyb3IoJ2JnJywgJ2RlYmUgc2VyIHVuIGNvbG9yIGhleCBkZSA2IGTDrWdpdG9zLCBlai4gI0ZGRkZGRicpO1xuICB9XG4gIGlmIChiLmRvdFN0eWxlICYmICFbJ3NxdWFyZScsICdyb3VuZGVkJywgJ2RvdHMnXS5pbmNsdWRlcyhiLmRvdFN0eWxlKSkge1xuICAgIHRocm93IG5ldyBRckJyYW5kVmFsaWRhdGlvbkVycm9yKCdkb3RTdHlsZScsICdkZWJlIHNlciBzcXVhcmUsIHJvdW5kZWQgbyBkb3RzJyk7XG4gIH1cbiAgY29uc3QgcmF0aW8gPSBxckNvbnRyYXN0UmF0aW8oYi5pbmssIGIuYmcpO1xuICBpZiAocmF0aW8gPCBNSU5fQ09OVFJBU1RfUkFUSU8pIHtcbiAgICB0aHJvdyBuZXcgUXJCcmFuZFZhbGlkYXRpb25FcnJvcihcbiAgICAgICdpbmsvYmcnLFxuICAgICAgYGNvbnRyYXN0ZSAke3JhdGlvLnRvRml4ZWQoMil9OjEgcG9yIGRlYmFqbyBkZWwgbcOtbmltbyAke01JTl9DT05UUkFTVF9SQVRJTy50b0ZpeGVkKDEpfToxIOKAlCB1biBsZWN0b3IgcmVhbCBubyB2YSBhIHBvZGVyIGVzY2FuZWFybG9gLFxuICAgICk7XG4gIH1cbiAgaWYgKGIubG9nbyAmJiAoYi5sb2dvLmNsZWFyQXJlYSA/PyAwKSA+IDAuMykge1xuICAgIHRocm93IG5ldyBRckJyYW5kVmFsaWRhdGlvbkVycm9yKFxuICAgICAgJ2xvZ28uY2xlYXJBcmVhJyxcbiAgICAgICd1biBsb2dvIHF1ZSB0YXBhIG3DoXMgZGVsIDMwJSBkZWwgUVIgZGVqYSBkZSBzZXIgbGVnaWJsZSBhdW5xdWUgc3ViYSBsYSBjb3JyZWNjacOzbiBkZSBlcnJvcmVzJyxcbiAgICApO1xuICB9XG59XG5cbi8qKlxuICogRWwgbG9nbyBvYmxpZ2EgYSBzdWJpciBsYSBjb3JyZWNjacOzbiBkZSBlcnJvcmVzLCBubyBzZSBsZSBwcmVndW50YSBhbFxuICogdXN1YXJpbyAoQURSLTA3OCByZWdsYSAyKS4gU2luIGxvZ28sIE0gYWxjYW56YS4gQ29uIGxvZ28sIHN1YmUgYSBROyBjb25cbiAqIGxvZ28gZ3JhbmRlICg+MTUlIGRlbCDDoXJlYSksIHN1YmUgYWwgbcOheGltbyBILlxuICovXG5leHBvcnQgZnVuY3Rpb24gcXJFcnJvckNvcnJlY3Rpb25Gb3IoYjogUXJCcmFuZCk6IFFyQnJhbmRFcnJvckNvcnJlY3Rpb24ge1xuICBjb25zdCBjbGVhckFyZWEgPSBiLmxvZ28/LmNsZWFyQXJlYSA/PyAwO1xuICBpZiAoIWIubG9nbyB8fCBjbGVhckFyZWEgPD0gMCkgcmV0dXJuICdNJztcbiAgaWYgKGNsZWFyQXJlYSA+IDAuMTUpIHJldHVybiAnSCc7XG4gIHJldHVybiAnUSc7XG59XG4iXX0=
@@ -2,5 +2,5 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export const VERSION = '4.0.939';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC45MzknO1xuIl19
5
+ export const VERSION = '4.0.940';
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC45NDAnO1xuIl19