@nice2dev/ui-security 1.0.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.
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Content Safety UI (SEC-001) — Safety dashboard, alert center,
3
+ * audit reports, rule editor, scanner config, age verification,
4
+ * identity verification, threat viewer, tenant safety config.
5
+ *
6
+ * PRO-5.13 — 9 items
7
+ *
8
+ * @module @nice2dev/ui-security/content-safety
9
+ */
10
+ export type IncidentSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
11
+ export interface SafetyIncident {
12
+ id: string;
13
+ /** Type of content flagged */
14
+ contentType: 'image' | 'video' | 'text' | 'audio' | 'file';
15
+ /** Category */
16
+ category: string;
17
+ severity: IncidentSeverity;
18
+ /** Status */
19
+ status: 'new' | 'reviewing' | 'confirmed' | 'false-positive' | 'resolved' | 'escalated';
20
+ /** Detected at */
21
+ detectedAt: string;
22
+ /** Source (user ID / upload ID) */
23
+ sourceId: string;
24
+ /** Confidence score (0-1) */
25
+ confidence: number;
26
+ /** Action taken */
27
+ actionTaken?: string;
28
+ /** Reviewer */
29
+ reviewerId?: string;
30
+ }
31
+ export interface SafetyDashboardStats {
32
+ /** Period */
33
+ periodFrom: string;
34
+ periodTo: string;
35
+ /** Total scanned */
36
+ totalScanned: number;
37
+ /** Total flagged */
38
+ totalFlagged: number;
39
+ /** By severity */
40
+ bySeverity: Record<IncidentSeverity, number>;
41
+ /** By category */
42
+ byCategory: Record<string, number>;
43
+ /** False positive rate (%) */
44
+ falsePositiveRate: number;
45
+ /** Average response time (ms) */
46
+ avgResponseTimeMs: number;
47
+ }
48
+ export type AlertAction = 'review' | 'block' | 'quarantine' | 'notify-admin' | 'auto-remove' | 'escalate';
49
+ export interface SafetyAlert {
50
+ id: string;
51
+ incidentId: string;
52
+ severity: IncidentSeverity;
53
+ title: string;
54
+ description: string;
55
+ /** Suggested action */
56
+ suggestedAction: AlertAction;
57
+ /** Created at */
58
+ createdAt: string;
59
+ /** Acknowledged */
60
+ acknowledged: boolean;
61
+ acknowledgedAt?: string;
62
+ acknowledgedBy?: string;
63
+ }
64
+ export interface SafetyAuditReport {
65
+ id: string;
66
+ /** Scan type */
67
+ scanType: 'full' | 'incremental' | 'targeted';
68
+ /** Start/end */
69
+ startedAt: string;
70
+ completedAt: string;
71
+ /** Content scanned */
72
+ totalScanned: number;
73
+ /** Findings */
74
+ findings: Array<{
75
+ category: string;
76
+ severity: IncidentSeverity;
77
+ count: number;
78
+ sampleIds: string[];
79
+ }>;
80
+ /** Compliance score (%) */
81
+ complianceScore: number;
82
+ /** Recommendations */
83
+ recommendations: string[];
84
+ }
85
+ export type RuleCategory = 'csam' | 'weapons' | 'drugs' | 'trafficking' | 'hate-speech' | 'violence' | 'nudity' | 'self-harm' | 'terrorism' | 'fraud' | 'spam' | 'copyright' | 'custom';
86
+ export interface SafetyRule {
87
+ id: string;
88
+ category: RuleCategory;
89
+ name: string;
90
+ description: string;
91
+ /** Enabled */
92
+ enabled: boolean;
93
+ /** Severity when triggered */
94
+ severity: IncidentSeverity;
95
+ /** Detection method */
96
+ method: 'ml-model' | 'regex' | 'hash-matching' | 'keyword' | 'external-api';
97
+ /** Threshold (0-1) */
98
+ threshold: number;
99
+ /** Auto-action */
100
+ autoAction: AlertAction;
101
+ /** Applicable content types */
102
+ contentTypes: Array<'image' | 'video' | 'text' | 'audio' | 'file'>;
103
+ }
104
+ export type ScannerProvider = 'internal-ml' | 'azure-content-safety' | 'aws-rekognition' | 'google-cloud-vision' | 'photodna' | 'custom';
105
+ export interface ScannerConfig {
106
+ id: string;
107
+ provider: ScannerProvider;
108
+ /** Enabled */
109
+ enabled: boolean;
110
+ /** API endpoint */
111
+ endpoint?: string;
112
+ /** Content types handled */
113
+ contentTypes: Array<'image' | 'video' | 'text' | 'audio' | 'file'>;
114
+ /** Categories handled */
115
+ categories: RuleCategory[];
116
+ /** Sensitivity level */
117
+ sensitivity: 'low' | 'medium' | 'high' | 'maximum';
118
+ /** Rate limit (requests/min) */
119
+ rateLimit: number;
120
+ /** Timeout (ms) */
121
+ timeout: number;
122
+ }
123
+ export type AgeVerificationMethod = 'self-declaration' | 'id-scan' | 'credit-card' | 'third-party' | 'digital-id' | 'bank-verification';
124
+ export interface AgeVerificationConfig {
125
+ /** Minimum age */
126
+ minimumAge: number;
127
+ /** Allowed methods */
128
+ methods: AgeVerificationMethod[];
129
+ /** Third-party provider */
130
+ provider?: string;
131
+ providerEndpoint?: string;
132
+ /** Remember verification (days) */
133
+ rememberDays: number;
134
+ /** Strict mode (require document) */
135
+ strictMode: boolean;
136
+ }
137
+ export type IdVerificationMethod = 'eID' | 'video-ident' | 'id-scan' | 'bank-id' | 'nfc-chip' | 'biometric' | 'manual-review';
138
+ export interface IdVerificationConfig {
139
+ /** KYC level */
140
+ kycLevel: 'basic' | 'standard' | 'enhanced';
141
+ /** Allowed methods */
142
+ methods: IdVerificationMethod[];
143
+ /** Provider */
144
+ provider: string;
145
+ providerEndpoint: string;
146
+ /** GDPR compliant */
147
+ gdprCompliant: boolean;
148
+ /** Document retention (days) */
149
+ retentionDays: number;
150
+ /** Accepted ID types */
151
+ acceptedDocuments: Array<'passport' | 'id-card' | 'drivers-license' | 'residence-permit'>;
152
+ }
153
+ export interface IdVerificationResult {
154
+ id: string;
155
+ status: 'pending' | 'verified' | 'failed' | 'expired';
156
+ method: IdVerificationMethod;
157
+ /** Verified name */
158
+ verifiedName?: string;
159
+ /** Verified date of birth */
160
+ verifiedDob?: string;
161
+ /** Verified nationality */
162
+ verifiedNationality?: string;
163
+ /** Confidence */
164
+ confidence: number;
165
+ /** Completed at */
166
+ completedAt?: string;
167
+ }
168
+ export interface ThreatDetail {
169
+ incidentId: string;
170
+ /** Content preview (blurred URL) */
171
+ blurredPreviewUrl?: string;
172
+ /** Metadata */
173
+ metadata: {
174
+ fileHash: string;
175
+ fileSize: number;
176
+ mimeType: string;
177
+ uploadedAt: string;
178
+ uploadedBy: string;
179
+ ipAddress?: string;
180
+ userAgent?: string;
181
+ };
182
+ /** Detection details */
183
+ detections: Array<{
184
+ scanner: string;
185
+ category: string;
186
+ confidence: number;
187
+ details: string;
188
+ }>;
189
+ /** Available actions */
190
+ actions: AlertAction[];
191
+ /** Related incidents */
192
+ relatedIncidentIds: string[];
193
+ }
194
+ export interface TenantSafetyConfig {
195
+ tenantId: string;
196
+ /** Strictness level */
197
+ strictnessLevel: 'permissive' | 'moderate' | 'strict' | 'maximum';
198
+ /** Enabled scanners */
199
+ enabledScannerIds: string[];
200
+ /** Custom thresholds per category */
201
+ thresholds: Record<RuleCategory, number>;
202
+ /** Auto-actions per category */
203
+ autoActions: Record<RuleCategory, AlertAction>;
204
+ /** Notification emails */
205
+ notificationEmails: string[];
206
+ /** Webhook URL */
207
+ webhookUrl?: string;
208
+ /** Quarantine enabled */
209
+ quarantineEnabled: boolean;
210
+ /** Appeal process */
211
+ appealEnabled: boolean;
212
+ }
@@ -0,0 +1,111 @@
1
+ import { BiometricConfig, BiometricTemplate, BiometricMethod, FingerprintFeatures, IrisFeatures, FaceFeatures, FaceLivenessChallenge } from './types';
2
+ export declare function resolveConfig(partial?: BiometricConfig): Required<BiometricConfig>;
3
+ /** Compute SHA-256 hash of a string (for integrity). */
4
+ export declare function sha256(data: string): Promise<string>;
5
+ /** Get device camera stream. */
6
+ export declare function getCameraStream(facingMode?: 'user' | 'environment'): Promise<MediaStream>;
7
+ /** Capture a single frame from a video element. */
8
+ export declare function captureFrame(video: HTMLVideoElement, width?: number, height?: number): {
9
+ canvas: HTMLCanvasElement;
10
+ ctx: CanvasRenderingContext2D;
11
+ imageData: ImageData;
12
+ };
13
+ /** Convert ImageData to grayscale. */
14
+ export declare function toGrayscale(imageData: ImageData): Uint8Array;
15
+ /** Apply Gaussian blur (3×3 kernel). */
16
+ export declare function gaussianBlur3x3(gray: Uint8Array, w: number, h: number): Uint8Array;
17
+ /** Sobel edge detection. Returns magnitude array. */
18
+ export declare function sobelEdges(gray: Uint8Array, w: number, h: number): Uint8Array;
19
+ /** Simple Otsu threshold. Returns binary mask. */
20
+ export declare function otsuThreshold(gray: Uint8Array): {
21
+ threshold: number;
22
+ binary: Uint8Array;
23
+ };
24
+ /** Extract minutiae points from a fingerprint image. */
25
+ export declare function extractMinutiae(imageData: ImageData, minQuality?: number): FingerprintFeatures;
26
+ /** Compare two fingerprint templates. Returns similarity 0-1. */
27
+ export declare function matchFingerprints(a: FingerprintFeatures, b: FingerprintFeatures): number;
28
+ /** Serialize fingerprint features to a storable template. */
29
+ export declare function fingerprintToTemplate(features: FingerprintFeatures, label?: string): Promise<BiometricTemplate>;
30
+ /** Deserialize a fingerprint template back to features. */
31
+ export declare function templateToFingerprint(template: BiometricTemplate): FingerprintFeatures;
32
+ /**
33
+ * Detect and segment iris from an eye image.
34
+ * Uses simplified Daugman's integro-differential operator approach:
35
+ * 1. Find pupil (dark circle)
36
+ * 2. Find iris boundary (outer ring)
37
+ * 3. Unwrap to polar (rubber-sheet normalisation)
38
+ * 4. Generate iris code via Gabor filter
39
+ */
40
+ export declare function detectIris(imageData: ImageData, eye?: 'left' | 'right'): IrisFeatures | null;
41
+ /**
42
+ * Compare two iris codes using Hamming distance.
43
+ * Returns similarity 0-1 (1 = identical).
44
+ */
45
+ export declare function matchIris(a: IrisFeatures, b: IrisFeatures): number;
46
+ /** Serialize iris features to template. */
47
+ export declare function irisToTemplate(features: IrisFeatures, label?: string): Promise<BiometricTemplate>;
48
+ export declare function templateToIris(template: BiometricTemplate): IrisFeatures;
49
+ /**
50
+ * Extract face features from an image.
51
+ * Uses a lightweight approach: skin-color detection + geometric features.
52
+ * For production, integrate a real model via ONNX or TensorFlow.js.
53
+ */
54
+ export declare function extractFaceFeatures(imageData: ImageData): FaceFeatures | null;
55
+ /** Compare two face embeddings. Returns similarity 0-1. */
56
+ export declare function matchFaces(a: FaceFeatures, b: FaceFeatures): number;
57
+ /** Compare a camera frame to a reference photo. */
58
+ export declare function compareFaceToPhoto(cameraFrame: ImageData, referencePhoto: ImageData): {
59
+ similarity: number;
60
+ cameraFeatures: FaceFeatures | null;
61
+ refFeatures: FaceFeatures | null;
62
+ };
63
+ export declare function faceToTemplate(features: FaceFeatures, label?: string): Promise<BiometricTemplate>;
64
+ export declare function templateToFace(template: BiometricTemplate): FaceFeatures;
65
+ export declare function generateLivenessChallenge(): FaceLivenessChallenge;
66
+ /**
67
+ * Check liveness by comparing consecutive frames.
68
+ * Detects if the face is a static photo (no movement) or a live person.
69
+ */
70
+ export declare function checkLiveness(frames: ImageData[], threshold?: number): {
71
+ isLive: boolean;
72
+ confidence: number;
73
+ motionScore: number;
74
+ };
75
+ export declare function collectDeviceInfo(): {
76
+ userAgent: string;
77
+ platform: string;
78
+ language: string;
79
+ screenResolution: [number, number];
80
+ timezone: string;
81
+ touchSupport: boolean;
82
+ hardwareConcurrency: number;
83
+ };
84
+ export declare function generateDeviceFingerprint(): Promise<string>;
85
+ export declare function isWebAuthnAvailable(): boolean;
86
+ export declare function isPasskeyAvailable(): Promise<boolean>;
87
+ export declare function registerWebAuthn(options: {
88
+ rpName: string;
89
+ rpId: string;
90
+ userId: string;
91
+ userName: string;
92
+ userDisplayName: string;
93
+ challenge: BufferSource;
94
+ timeout?: number;
95
+ }): Promise<PublicKeyCredential | null>;
96
+ export declare function authenticateWebAuthn(options: {
97
+ rpId: string;
98
+ challenge: BufferSource;
99
+ allowCredentials?: PublicKeyCredentialDescriptor[];
100
+ timeout?: number;
101
+ }): Promise<PublicKeyCredential | null>;
102
+ export declare function verifyWithBackend(endpoint: string, payload: {
103
+ method: BiometricMethod;
104
+ templateData: string;
105
+ matchScore: number;
106
+ deviceFingerprint?: string;
107
+ }, authToken?: string): Promise<{
108
+ verified: boolean;
109
+ serverScore?: number;
110
+ message?: string;
111
+ }>;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ export type NiceTranslateFn = (key: string, defaultValue: string) => string;
3
+ export interface NiceI18nProviderProps {
4
+ /** Your translate function, e.g. the `t` from react-i18next. */
5
+ t: NiceTranslateFn;
6
+ children: React.ReactNode;
7
+ }
8
+ export declare const NiceI18nProvider: React.FC<NiceI18nProviderProps>;
9
+ export declare function useNiceTranslation(): {
10
+ t: NiceTranslateFn;
11
+ };
@@ -0,0 +1,244 @@
1
+ /**
2
+ * @nice2dev/ui-security — Shared types
3
+ *
4
+ * Common type definitions used across all security components.
5
+ */
6
+ export type SecurityVerificationStatus = 'idle' | 'scanning' | 'processing' | 'success' | 'failure' | 'error' | 'timeout';
7
+ export type SecurityLevel = 'low' | 'medium' | 'high' | 'critical';
8
+ export interface SecurityEvent {
9
+ type: string;
10
+ timestamp: number;
11
+ status: SecurityVerificationStatus;
12
+ details?: Record<string, unknown>;
13
+ deviceInfo?: DeviceInfo;
14
+ }
15
+ export interface DeviceInfo {
16
+ userAgent: string;
17
+ platform: string;
18
+ language: string;
19
+ screenResolution: [number, number];
20
+ timezone: string;
21
+ touchSupport: boolean;
22
+ webGLRenderer?: string;
23
+ hardwareConcurrency?: number;
24
+ }
25
+ export interface VerificationResult {
26
+ success: boolean;
27
+ confidence: number;
28
+ method: BiometricMethod;
29
+ timestamp: number;
30
+ duration: number;
31
+ livenessPassed?: boolean;
32
+ /** Raw data for optional backend verification. */
33
+ payload?: string;
34
+ /** Encrypted data for backend. */
35
+ encryptedPayload?: string;
36
+ error?: string;
37
+ }
38
+ export type BiometricMethod = 'fingerprint' | 'iris' | 'face' | 'voice' | 'webauthn' | 'pattern' | 'pin' | 'passphrase';
39
+ export interface BiometricTemplate {
40
+ id: string;
41
+ method: BiometricMethod;
42
+ /** Base64-encoded template data. */
43
+ templateData: string;
44
+ /** When the template was enrolled. */
45
+ enrolled: number;
46
+ /** Human-readable label, e.g. "Left index finger". */
47
+ label?: string;
48
+ /** SHA-256 hash of raw template for integrity. */
49
+ hash?: string;
50
+ }
51
+ export interface BiometricConfig {
52
+ /** Minimum confidence threshold for a match (0-1). Default: 0.85 */
53
+ matchThreshold?: number;
54
+ /** Maximum time to wait for scan in ms. Default: 30000 */
55
+ timeout?: number;
56
+ /** Number of retry attempts. Default: 3 */
57
+ maxRetries?: number;
58
+ /** Enable liveness detection. Default: true */
59
+ livenessDetection?: boolean;
60
+ /** Optional backend URL for server-side verification. */
61
+ verifyEndpoint?: string;
62
+ /** JWT or API key for backend calls. */
63
+ authToken?: string;
64
+ /** Show visual feedback during scan. Default: true */
65
+ showFeedback?: boolean;
66
+ /** Vibrate on events (mobile). Default: true */
67
+ hapticFeedback?: boolean;
68
+ /** Language/locale override. */
69
+ locale?: string;
70
+ /** Security level. Higher = stricter thresholds. Default: 'high' */
71
+ securityLevel?: SecurityLevel;
72
+ /** Enable anti-spoofing checks. Default: true */
73
+ antiSpoofing?: boolean;
74
+ }
75
+ export type FingerPosition = 'left-thumb' | 'left-index' | 'left-middle' | 'left-ring' | 'left-little' | 'right-thumb' | 'right-index' | 'right-middle' | 'right-ring' | 'right-little';
76
+ export interface MinutiaePoint {
77
+ x: number;
78
+ y: number;
79
+ angle: number;
80
+ type: 'ending' | 'bifurcation' | 'dot' | 'island' | 'spur';
81
+ quality: number;
82
+ }
83
+ export interface FingerprintFeatures {
84
+ minutiae: MinutiaePoint[];
85
+ corePoints: {
86
+ x: number;
87
+ y: number;
88
+ }[];
89
+ deltaPoints: {
90
+ x: number;
91
+ y: number;
92
+ }[];
93
+ ridgeCount: number;
94
+ patternType: 'arch' | 'loop-left' | 'loop-right' | 'whorl' | 'composite';
95
+ quality: number;
96
+ }
97
+ export type EyePosition = 'left' | 'right' | 'both';
98
+ export interface IrisFeatures {
99
+ /** Normalized iris code (binary template). */
100
+ irisCode: string;
101
+ /** Pupil centre and radius. */
102
+ pupil: {
103
+ cx: number;
104
+ cy: number;
105
+ r: number;
106
+ };
107
+ /** Iris outer boundary. */
108
+ iris: {
109
+ cx: number;
110
+ cy: number;
111
+ r: number;
112
+ };
113
+ /** Hamming distance score 0-1 (lower = better match). */
114
+ hammingDistance?: number;
115
+ /** Quality score 0-1. */
116
+ quality: number;
117
+ /** Which eye. */
118
+ eye: EyePosition;
119
+ }
120
+ export interface FaceFeatures {
121
+ /** 128-dimensional face embedding. */
122
+ embedding: number[];
123
+ /** Bounding box of detected face. */
124
+ boundingBox: {
125
+ x: number;
126
+ y: number;
127
+ width: number;
128
+ height: number;
129
+ };
130
+ /** Facial landmarks (68 points). */
131
+ landmarks: {
132
+ x: number;
133
+ y: number;
134
+ }[];
135
+ /** Head pose angles. */
136
+ headPose: {
137
+ yaw: number;
138
+ pitch: number;
139
+ roll: number;
140
+ };
141
+ /** Quality score. */
142
+ quality: number;
143
+ /** Liveness confidence. */
144
+ livenessScore?: number;
145
+ }
146
+ export interface FaceLivenessChallenge {
147
+ type: 'blink' | 'smile' | 'turn-left' | 'turn-right' | 'nod' | 'raise-eyebrows';
148
+ instruction: string;
149
+ completed: boolean;
150
+ confidence: number;
151
+ }
152
+ export interface KeypadConfig {
153
+ /** Number of digits for PIN. Default: 4 */
154
+ length?: number;
155
+ /** Maximum length. Default: same as length */
156
+ maxLength?: number;
157
+ /** Randomise key positions. Default: false */
158
+ scramble?: boolean;
159
+ /** Show entered digits briefly. Default: false */
160
+ showDigits?: boolean;
161
+ /** Time before auto-clear in ms. Default: none */
162
+ autoClearTimeout?: number;
163
+ /** Allow biometric fallback. Default: false */
164
+ biometricFallback?: boolean;
165
+ /** Maximum attempts before lockout. Default: 5 */
166
+ maxAttempts?: number;
167
+ /** Lockout duration in ms. Default: 30000 */
168
+ lockoutDuration?: number;
169
+ /** Key size in px. */
170
+ keySize?: number;
171
+ /** Enable haptic feedback. Default: true */
172
+ haptic?: boolean;
173
+ /** Custom key labels (e.g. for phone-style letters). */
174
+ keyLabels?: Record<string, string>;
175
+ }
176
+ export interface PatternLockConfig {
177
+ /** Grid size (e.g. 3 = 3x3). Default: 3 */
178
+ gridSize?: number;
179
+ /** Minimum pattern length. Default: 4 */
180
+ minLength?: number;
181
+ /** Show pattern trail. Default: true */
182
+ showTrail?: boolean;
183
+ /** Show error animation. Default: true */
184
+ showError?: boolean;
185
+ /** Max attempts before lockout. Default: 5 */
186
+ maxAttempts?: number;
187
+ /** Lockout duration in ms. Default: 30000 */
188
+ lockoutDuration?: number;
189
+ /** Dot size in px. Default: 16 */
190
+ dotSize?: number;
191
+ /** Active dot size in px. Default: 24 */
192
+ activeDotSize?: number;
193
+ /** Trail color. Default: var(--security-accent) */
194
+ trailColor?: string;
195
+ }
196
+ export interface WebAuthnConfig {
197
+ /** Relying party name. */
198
+ rpName: string;
199
+ /** Relying party ID (domain). */
200
+ rpId: string;
201
+ /** User info for registration. */
202
+ user?: {
203
+ id: string;
204
+ name: string;
205
+ displayName: string;
206
+ };
207
+ /** Attestation preference. Default: 'none' */
208
+ attestation?: AttestationConveyancePreference;
209
+ /** Authenticator preference. Default: 'platform' */
210
+ authenticatorAttachment?: AuthenticatorAttachment;
211
+ /** Require resident key. Default: 'preferred' */
212
+ residentKey?: ResidentKeyRequirement;
213
+ /** User verification. Default: 'required' */
214
+ userVerification?: UserVerificationRequirement;
215
+ /** Timeout in ms. Default: 60000 */
216
+ timeout?: number;
217
+ }
218
+ export interface SecurityAuditEntry {
219
+ id: string;
220
+ timestamp: number;
221
+ action: SecurityAuditAction;
222
+ method?: BiometricMethod;
223
+ status: 'success' | 'failure' | 'error';
224
+ ip?: string;
225
+ device?: string;
226
+ location?: string;
227
+ details?: string;
228
+ riskScore?: number;
229
+ }
230
+ export type SecurityAuditAction = 'login' | 'logout' | 'enroll-biometric' | 'verify-biometric' | 'pin-entry' | 'pattern-entry' | 'passphrase-entry' | 'webauthn-register' | 'webauthn-authenticate' | 'lockout' | 'unlock' | 'password-change' | 'mfa-setup' | 'mfa-verify' | 'session-expired' | 'suspicious-activity';
231
+ export interface DeviceTrustScore {
232
+ score: number;
233
+ factors: DeviceTrustFactor[];
234
+ trusted: boolean;
235
+ firstSeen: number;
236
+ lastSeen: number;
237
+ fingerprint: string;
238
+ }
239
+ export interface DeviceTrustFactor {
240
+ name: string;
241
+ score: number;
242
+ description: string;
243
+ weight: number;
244
+ }