@mikashboks/capture-intelligence-core 0.2.3 → 0.2.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/dist/cjs/capture-guidance.d.ts +1 -1
- package/dist/cjs/capture-guidance.d.ts.map +1 -1
- package/dist/cjs/capture-guidance.js +4 -6
- package/dist/cjs/capture-guidance.js.map +1 -1
- package/dist/cjs/capture-trust.d.ts +4 -0
- package/dist/cjs/capture-trust.d.ts.map +1 -1
- package/dist/cjs/capture-trust.js +11 -2
- package/dist/cjs/capture-trust.js.map +1 -1
- package/dist/cjs/document-crop-arbitration.d.ts +105 -0
- package/dist/cjs/document-crop-arbitration.d.ts.map +1 -0
- package/dist/cjs/document-crop-arbitration.js +660 -0
- package/dist/cjs/document-crop-arbitration.js.map +1 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +27 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/capture-guidance.d.ts +1 -1
- package/dist/esm/capture-guidance.d.ts.map +1 -1
- package/dist/esm/capture-guidance.js +4 -6
- package/dist/esm/capture-guidance.js.map +1 -1
- package/dist/esm/capture-trust.d.ts +4 -0
- package/dist/esm/capture-trust.d.ts.map +1 -1
- package/dist/esm/capture-trust.js +11 -2
- package/dist/esm/capture-trust.js.map +1 -1
- package/dist/esm/document-crop-arbitration.d.ts +105 -0
- package/dist/esm/document-crop-arbitration.d.ts.map +1 -0
- package/dist/esm/document-crop-arbitration.js +642 -0
- package/dist/esm/document-crop-arbitration.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
import { decideCaptureTrust } from './capture-trust.js';
|
|
2
|
+
export const DEFAULT_CROP_CONFIDENCE_THRESHOLD = 0.8;
|
|
3
|
+
export const DEFAULT_RECTANGLE_CROP_CONFIDENCE_THRESHOLD = 0.88;
|
|
4
|
+
export const DEFAULT_SUBMIT_CROP_INSET_FLOOR_ENABLED = true;
|
|
5
|
+
export const DOCUMENT_CROP_ASPECT_MIN = 0.45;
|
|
6
|
+
export const DOCUMENT_CROP_ASPECT_MAX = 2.15;
|
|
7
|
+
export const CARD_LIKE_MEASURED_CROP_MIN_AREA_RATIO = 0.06;
|
|
8
|
+
export const QUIET_ZONE_CLAMP_CUT_OFF_RISK_LOSS = 0.8;
|
|
9
|
+
export const SAFE_LIVE_QUAD_INSET_MIN = 0.02;
|
|
10
|
+
const DOCUMENT_CROP_VISIBLE_EDGE_INSET_RATIO = 0.02;
|
|
11
|
+
const REVIEW_AUTO_CROP_VISIBLE_EDGE_INSET_RATIO = 0.05;
|
|
12
|
+
const DEFAULT_SUBMIT_CROP_VISIBLE_EDGE_INSET_RATIO = 0.055;
|
|
13
|
+
const TRUSTED_LIVE_BOUNDS_MIN_AREA_RATIO = 0.035;
|
|
14
|
+
const DEFAULT_CROP_OUTPUT_MIN_LONG_SIDE = 900;
|
|
15
|
+
const DEFAULT_CROP_OUTPUT_MIN_SHORT_SIDE = 500;
|
|
16
|
+
const TRUSTED_LIVE_BOUNDS_CROP_OUTPUT_MIN_LONG_SIDE = 520;
|
|
17
|
+
const TRUSTED_LIVE_BOUNDS_CROP_OUTPUT_MIN_SHORT_SIDE = 300;
|
|
18
|
+
const CAPTURE_CANDIDATE_HIGH_CLASSIFIER_CONFIDENCE = 70;
|
|
19
|
+
const CAPTURE_CANDIDATE_MRZ_CONFIRMED_CONFIDENCE_MIN = 0.45;
|
|
20
|
+
const NATIVE_OVERFULL_AREA_RATIO_MIN = 0.92;
|
|
21
|
+
const EXPLICIT_DOCUMENT_CUT_OFF_REASONS = new Set([
|
|
22
|
+
'cut_off',
|
|
23
|
+
'cutoff',
|
|
24
|
+
'document_cut_off',
|
|
25
|
+
'document_cutoff',
|
|
26
|
+
'edge_cut_off',
|
|
27
|
+
'missing_edge',
|
|
28
|
+
'missing_edges',
|
|
29
|
+
'partially_cropped',
|
|
30
|
+
'border_touching',
|
|
31
|
+
'border_touched',
|
|
32
|
+
'touching_border',
|
|
33
|
+
]);
|
|
34
|
+
const EXPLICIT_UNSAFE_CAPTURE_REASONS = new Set([
|
|
35
|
+
...EXPLICIT_DOCUMENT_CUT_OFF_REASONS,
|
|
36
|
+
'unsafe_crop',
|
|
37
|
+
'unsafe_crop_rejected',
|
|
38
|
+
'crop_too_narrow',
|
|
39
|
+
'too_narrow',
|
|
40
|
+
]);
|
|
41
|
+
const DOCUMENT_CROP_SAFETY_THRESHOLDS_BY_FAMILY = {
|
|
42
|
+
'card-like': {
|
|
43
|
+
minAreaRatio: 0.2,
|
|
44
|
+
minWidthRatio: 0.18,
|
|
45
|
+
minHeightRatio: 0.12,
|
|
46
|
+
maxHorizontalOutsideMarginRatio: 0.62,
|
|
47
|
+
maxVerticalOutsideMarginRatio: 0.62,
|
|
48
|
+
aspectMin: 1.1,
|
|
49
|
+
aspectMax: DOCUMENT_CROP_ASPECT_MAX,
|
|
50
|
+
},
|
|
51
|
+
'passport-like': {
|
|
52
|
+
minAreaRatio: 0.1,
|
|
53
|
+
minWidthRatio: 0.18,
|
|
54
|
+
minHeightRatio: 0.18,
|
|
55
|
+
maxHorizontalOutsideMarginRatio: 0.55,
|
|
56
|
+
maxVerticalOutsideMarginRatio: 0.55,
|
|
57
|
+
aspectMin: 1.05,
|
|
58
|
+
aspectMax: 1.85,
|
|
59
|
+
useNormalizedAspect: true,
|
|
60
|
+
},
|
|
61
|
+
'document-page-like': {
|
|
62
|
+
minAreaRatio: 0.14,
|
|
63
|
+
minWidthRatio: 0.22,
|
|
64
|
+
minHeightRatio: 0.18,
|
|
65
|
+
maxHorizontalOutsideMarginRatio: 0.48,
|
|
66
|
+
maxVerticalOutsideMarginRatio: 0.48,
|
|
67
|
+
aspectMin: 0.6,
|
|
68
|
+
aspectMax: 1.65,
|
|
69
|
+
useNormalizedAspect: true,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
73
|
+
const normalizeReason = (reason) => (reason ?? '').trim().toLowerCase();
|
|
74
|
+
export const isCardLikeCropAspect = (aspectRatio) => Number.isFinite(aspectRatio) && aspectRatio >= 1.1 && aspectRatio <= DOCUMENT_CROP_ASPECT_MAX;
|
|
75
|
+
export const getDocumentCropShapeFamily = (aspectRatio) => {
|
|
76
|
+
if (isCardLikeCropAspect(aspectRatio))
|
|
77
|
+
return 'card-like';
|
|
78
|
+
if (Number.isFinite(aspectRatio) && aspectRatio >= 0.6 && aspectRatio <= 1.85) {
|
|
79
|
+
return 'passport-like';
|
|
80
|
+
}
|
|
81
|
+
return 'document-page-like';
|
|
82
|
+
};
|
|
83
|
+
export const getDocumentCropSafetyThresholds = (aspectRatio, shapeFamily) => {
|
|
84
|
+
return DOCUMENT_CROP_SAFETY_THRESHOLDS_BY_FAMILY[shapeFamily ?? getDocumentCropShapeFamily(aspectRatio)];
|
|
85
|
+
};
|
|
86
|
+
export const getDocumentCropSafetyAspectRatio = (aspectRatio, safetyThresholds) => safetyThresholds.useNormalizedAspect && Number.isFinite(aspectRatio) && aspectRatio > 0
|
|
87
|
+
? Math.max(aspectRatio, 1 / aspectRatio)
|
|
88
|
+
: aspectRatio;
|
|
89
|
+
export const isDocumentCropAspectSafe = (aspectRatio, safetyThresholds) => {
|
|
90
|
+
const effectiveAspectRatio = getDocumentCropSafetyAspectRatio(aspectRatio, safetyThresholds);
|
|
91
|
+
return (Number.isFinite(effectiveAspectRatio) &&
|
|
92
|
+
effectiveAspectRatio >= safetyThresholds.aspectMin &&
|
|
93
|
+
effectiveAspectRatio <= safetyThresholds.aspectMax);
|
|
94
|
+
};
|
|
95
|
+
export const getMeasuredDocumentCropMinAreaRatio = (aspectRatio, shapeFamily) => {
|
|
96
|
+
const resolvedShapeFamily = shapeFamily ?? getDocumentCropShapeFamily(aspectRatio);
|
|
97
|
+
const safetyThresholds = getDocumentCropSafetyThresholds(aspectRatio, resolvedShapeFamily);
|
|
98
|
+
if (resolvedShapeFamily === 'card-like' &&
|
|
99
|
+
isDocumentCropAspectSafe(aspectRatio, safetyThresholds)) {
|
|
100
|
+
return Math.min(safetyThresholds.minAreaRatio, CARD_LIKE_MEASURED_CROP_MIN_AREA_RATIO);
|
|
101
|
+
}
|
|
102
|
+
return safetyThresholds.minAreaRatio;
|
|
103
|
+
};
|
|
104
|
+
export const hasExplicitDocumentCutOffReason = (reason) => EXPLICIT_DOCUMENT_CUT_OFF_REASONS.has(normalizeReason(reason));
|
|
105
|
+
export const hasExplicitUnsafeCaptureReason = (reason) => EXPLICIT_UNSAFE_CAPTURE_REASONS.has(normalizeReason(reason));
|
|
106
|
+
export const hasCaptureDocumentCutOffEvidence = (metadata) => {
|
|
107
|
+
if (!metadata)
|
|
108
|
+
return false;
|
|
109
|
+
return (hasExplicitDocumentCutOffReason(metadata.reason) ||
|
|
110
|
+
hasExplicitUnsafeCaptureReason(metadata.reason));
|
|
111
|
+
};
|
|
112
|
+
const isContourBackedDocumentBoundsDetector = (detector) => detector === 'scanic' || detector === 'kt-jscanify';
|
|
113
|
+
const isNativeViewfinderCrop = (metadata) => Boolean(metadata &&
|
|
114
|
+
(metadata.cropSource === 'viewfinder_candidate' ||
|
|
115
|
+
metadata.scannerEngine === 'native-viewfinder' ||
|
|
116
|
+
metadata.reason === 'web_native_viewfinder_capture'));
|
|
117
|
+
const isMeasuredCardViewfinderCrop = (metadata) => metadata.scannerEngine === 'native-viewfinder' &&
|
|
118
|
+
metadata.cropSource === 'viewfinder_candidate' &&
|
|
119
|
+
metadata.cropShapeFamily === 'card-like';
|
|
120
|
+
const isTrustedLiveBoundsCrop = (metadata) => metadata.cropSource === 'live_quad_candidate' &&
|
|
121
|
+
metadata.cropVerified === true &&
|
|
122
|
+
metadata.cornersOk === true &&
|
|
123
|
+
metadata.liveBoundsReliable !== false &&
|
|
124
|
+
isFiniteNumber(metadata.liveQuadInsetMin) &&
|
|
125
|
+
metadata.liveQuadInsetMin >= SAFE_LIVE_QUAD_INSET_MIN &&
|
|
126
|
+
(metadata.cropShapeFamily === 'card-like' || metadata.cropShapeFamily === 'passport-like');
|
|
127
|
+
const isMrzAnchoredPassportDataPageCrop = (metadata) => metadata.cropSource === 'mrz_anchor_candidate' &&
|
|
128
|
+
metadata.reason === 'detected_passport_data_page' &&
|
|
129
|
+
metadata.cornersOk === true &&
|
|
130
|
+
metadata.cropShapeFamily === 'passport-like';
|
|
131
|
+
const hasUnsafeLiveQuadCropEvidence = (metadata) => Boolean(metadata.cropSource === 'live_quad_candidate' &&
|
|
132
|
+
metadata.liveQuadPresent !== false &&
|
|
133
|
+
isFiniteNumber(metadata.liveQuadInsetMin) &&
|
|
134
|
+
metadata.liveQuadInsetMin < SAFE_LIVE_QUAD_INSET_MIN);
|
|
135
|
+
const isReviewAutoCrop = (metadata) => metadata.cropProvenance === 'review_auto_crop' || metadata.cropSource === 'review_auto_crop';
|
|
136
|
+
const getDefaultableVisibleEdgeInsetRatio = (metadata) => isReviewAutoCrop(metadata)
|
|
137
|
+
? REVIEW_AUTO_CROP_VISIBLE_EDGE_INSET_RATIO
|
|
138
|
+
: DOCUMENT_CROP_VISIBLE_EDGE_INSET_RATIO;
|
|
139
|
+
const hasQuietZoneClampCutOffEvidence = (metadata) => Boolean(metadata &&
|
|
140
|
+
isFiniteNumber(metadata.quietZoneClampLossFraction) &&
|
|
141
|
+
metadata.quietZoneClampLossFraction >= QUIET_ZONE_CLAMP_CUT_OFF_RISK_LOSS);
|
|
142
|
+
const getCropAreaRatio = (rect, sourceImageSize) => (rect.w * rect.h) / Math.max(1, sourceImageSize.width * sourceImageSize.height);
|
|
143
|
+
const getCropAspectRatio = (rect) => (rect.h > 0 ? rect.w / rect.h : 0);
|
|
144
|
+
const getCropMinAreaRatio = (metadata, aspectRatio, shapeFamily) => {
|
|
145
|
+
if (isTrustedLiveBoundsCrop(metadata)) {
|
|
146
|
+
return Math.min(getDocumentCropSafetyThresholds(aspectRatio, shapeFamily).minAreaRatio, TRUSTED_LIVE_BOUNDS_MIN_AREA_RATIO);
|
|
147
|
+
}
|
|
148
|
+
if (isMeasuredCardViewfinderCrop(metadata)) {
|
|
149
|
+
return getMeasuredDocumentCropMinAreaRatio(aspectRatio, shapeFamily);
|
|
150
|
+
}
|
|
151
|
+
return getDocumentCropSafetyThresholds(aspectRatio, shapeFamily).minAreaRatio;
|
|
152
|
+
};
|
|
153
|
+
const hasDocumentCropGeometry = (metadata) => Boolean(metadata.rect && metadata.sourceImageSize);
|
|
154
|
+
const getCropSafetyInputs = (metadata) => {
|
|
155
|
+
const { rect, sourceImageSize } = metadata;
|
|
156
|
+
if (!rect || !sourceImageSize)
|
|
157
|
+
return null;
|
|
158
|
+
if (!isFiniteNumber(sourceImageSize.width) ||
|
|
159
|
+
!isFiniteNumber(sourceImageSize.height) ||
|
|
160
|
+
sourceImageSize.width <= 0 ||
|
|
161
|
+
sourceImageSize.height <= 0 ||
|
|
162
|
+
!isFiniteNumber(rect.x) ||
|
|
163
|
+
!isFiniteNumber(rect.y) ||
|
|
164
|
+
!isFiniteNumber(rect.w) ||
|
|
165
|
+
!isFiniteNumber(rect.h) ||
|
|
166
|
+
rect.w <= 0 ||
|
|
167
|
+
rect.h <= 0) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const width = Math.max(1, sourceImageSize.width);
|
|
171
|
+
const height = Math.max(1, sourceImageSize.height);
|
|
172
|
+
const widthRatio = rect.w / width;
|
|
173
|
+
const heightRatio = rect.h / height;
|
|
174
|
+
const leftMarginRatio = rect.x / width;
|
|
175
|
+
const topMarginRatio = rect.y / height;
|
|
176
|
+
const rightMarginRatio = (sourceImageSize.width - (rect.x + rect.w)) / width;
|
|
177
|
+
const bottomMarginRatio = (sourceImageSize.height - (rect.y + rect.h)) / height;
|
|
178
|
+
const areaRatio = isFiniteNumber(metadata.areaRatio)
|
|
179
|
+
? metadata.areaRatio
|
|
180
|
+
: getCropAreaRatio(rect, sourceImageSize);
|
|
181
|
+
const aspectRatio = isFiniteNumber(metadata.aspectRatio)
|
|
182
|
+
? metadata.aspectRatio
|
|
183
|
+
: getCropAspectRatio(rect);
|
|
184
|
+
return {
|
|
185
|
+
rect,
|
|
186
|
+
sourceImageSize,
|
|
187
|
+
widthRatio,
|
|
188
|
+
heightRatio,
|
|
189
|
+
leftMarginRatio,
|
|
190
|
+
topMarginRatio,
|
|
191
|
+
rightMarginRatio,
|
|
192
|
+
bottomMarginRatio,
|
|
193
|
+
areaRatio,
|
|
194
|
+
aspectRatio,
|
|
195
|
+
shapeFamily: metadata.cropShapeFamily,
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
const isDocumentCropGeometrySafe = (metadata) => {
|
|
199
|
+
if (metadata.clamped || metadata.unsafeReason)
|
|
200
|
+
return false;
|
|
201
|
+
const safetyInputs = getCropSafetyInputs(metadata);
|
|
202
|
+
if (!safetyInputs)
|
|
203
|
+
return false;
|
|
204
|
+
const { widthRatio, heightRatio, leftMarginRatio, topMarginRatio, rightMarginRatio, bottomMarginRatio, areaRatio, aspectRatio, shapeFamily, } = safetyInputs;
|
|
205
|
+
const safetyThresholds = getDocumentCropSafetyThresholds(aspectRatio, shapeFamily);
|
|
206
|
+
const minAreaRatio = getCropMinAreaRatio(metadata, aspectRatio, shapeFamily);
|
|
207
|
+
if (!isFiniteNumber(areaRatio) || !isFiniteNumber(aspectRatio))
|
|
208
|
+
return false;
|
|
209
|
+
if (areaRatio < minAreaRatio)
|
|
210
|
+
return false;
|
|
211
|
+
if (widthRatio < safetyThresholds.minWidthRatio ||
|
|
212
|
+
heightRatio < safetyThresholds.minHeightRatio) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
if (!isDocumentCropAspectSafe(aspectRatio, safetyThresholds))
|
|
216
|
+
return false;
|
|
217
|
+
if (leftMarginRatio < 0 ||
|
|
218
|
+
rightMarginRatio < 0 ||
|
|
219
|
+
topMarginRatio < 0 ||
|
|
220
|
+
bottomMarginRatio < 0 ||
|
|
221
|
+
leftMarginRatio > safetyThresholds.maxHorizontalOutsideMarginRatio ||
|
|
222
|
+
rightMarginRatio > safetyThresholds.maxHorizontalOutsideMarginRatio ||
|
|
223
|
+
topMarginRatio > safetyThresholds.maxVerticalOutsideMarginRatio ||
|
|
224
|
+
bottomMarginRatio > safetyThresholds.maxVerticalOutsideMarginRatio) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
};
|
|
229
|
+
const getPointInsetRatio = (point, sourceImageSize) => Math.min(point.x / Math.max(1, sourceImageSize.width), point.y / Math.max(1, sourceImageSize.height), (sourceImageSize.width - point.x) / Math.max(1, sourceImageSize.width), (sourceImageSize.height - point.y) / Math.max(1, sourceImageSize.height));
|
|
230
|
+
const getMinimumPolygonInsetRatio = (metadata) => {
|
|
231
|
+
const { polygon, sourceImageSize } = metadata;
|
|
232
|
+
if (!polygon || !sourceImageSize)
|
|
233
|
+
return null;
|
|
234
|
+
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
235
|
+
if (points.some(point => !isFiniteNumber(point.x) || !isFiniteNumber(point.y)) ||
|
|
236
|
+
!isFiniteNumber(sourceImageSize.width) ||
|
|
237
|
+
!isFiniteNumber(sourceImageSize.height) ||
|
|
238
|
+
sourceImageSize.width <= 0 ||
|
|
239
|
+
sourceImageSize.height <= 0) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return Math.min(...points.map(point => getPointInsetRatio(point, sourceImageSize)));
|
|
243
|
+
};
|
|
244
|
+
const getMinimumCropInsetRatio = (metadata) => {
|
|
245
|
+
const polygonInsetRatio = getMinimumPolygonInsetRatio(metadata);
|
|
246
|
+
if (polygonInsetRatio !== null)
|
|
247
|
+
return polygonInsetRatio;
|
|
248
|
+
if (!isReviewAutoCrop(metadata))
|
|
249
|
+
return null;
|
|
250
|
+
const safetyInputs = getCropSafetyInputs(metadata);
|
|
251
|
+
if (!safetyInputs)
|
|
252
|
+
return null;
|
|
253
|
+
return Math.min(safetyInputs.leftMarginRatio, safetyInputs.topMarginRatio, safetyInputs.rightMarginRatio, safetyInputs.bottomMarginRatio);
|
|
254
|
+
};
|
|
255
|
+
const getMinimumCropBoundsInsetRatio = (metadata) => {
|
|
256
|
+
const polygonInsetRatio = getMinimumPolygonInsetRatio(metadata);
|
|
257
|
+
if (polygonInsetRatio !== null)
|
|
258
|
+
return polygonInsetRatio;
|
|
259
|
+
const safetyInputs = getCropSafetyInputs(metadata);
|
|
260
|
+
if (!safetyInputs)
|
|
261
|
+
return null;
|
|
262
|
+
return Math.min(safetyInputs.leftMarginRatio, safetyInputs.topMarginRatio, safetyInputs.rightMarginRatio, safetyInputs.bottomMarginRatio);
|
|
263
|
+
};
|
|
264
|
+
export const hasMarginSafeDocumentCropEvidence = (metadata) => {
|
|
265
|
+
const minimumInsetRatio = getMinimumCropInsetRatio(metadata);
|
|
266
|
+
const minimumRequiredInsetRatio = getDefaultableVisibleEdgeInsetRatio(metadata);
|
|
267
|
+
return Boolean((metadata.confidence ?? 0) >= 0.6 &&
|
|
268
|
+
minimumInsetRatio !== null &&
|
|
269
|
+
minimumInsetRatio >= minimumRequiredInsetRatio &&
|
|
270
|
+
isDocumentCropGeometrySafe(metadata));
|
|
271
|
+
};
|
|
272
|
+
const hasMarginSafeReliableCaptureQuad = (metadata) => Boolean(metadata?.liveQuadPresent &&
|
|
273
|
+
isContourBackedDocumentBoundsDetector(metadata.liveBoundsDetector) &&
|
|
274
|
+
metadata.liveBoundsReliable !== false &&
|
|
275
|
+
isFiniteNumber(metadata.liveQuadInsetMin) &&
|
|
276
|
+
metadata.liveQuadInsetMin >= SAFE_LIVE_QUAD_INSET_MIN);
|
|
277
|
+
const hasUnsafeInsetReliableCaptureQuad = (metadata) => Boolean(metadata?.liveQuadPresent &&
|
|
278
|
+
isContourBackedDocumentBoundsDetector(metadata.liveBoundsDetector) &&
|
|
279
|
+
metadata.liveBoundsReliable !== false &&
|
|
280
|
+
isFiniteNumber(metadata.liveQuadInsetMin) &&
|
|
281
|
+
metadata.liveQuadInsetMin < SAFE_LIVE_QUAD_INSET_MIN);
|
|
282
|
+
const normalizeEvidenceKey = (value) => String(value || '')
|
|
283
|
+
.trim()
|
|
284
|
+
.toLowerCase()
|
|
285
|
+
.replace(/[\s-]+/g, '_');
|
|
286
|
+
const isBackSideStrategy = (strategy) => normalizeEvidenceKey(strategy).endsWith('_back');
|
|
287
|
+
const isCardFrontCaptureContext = (metadata) => {
|
|
288
|
+
if (metadata?.cropShapeFamily !== 'card-like')
|
|
289
|
+
return false;
|
|
290
|
+
if (isBackSideStrategy(metadata?.liveRecognitionStrategy))
|
|
291
|
+
return false;
|
|
292
|
+
if (metadata?.liveRecognitionStrategy)
|
|
293
|
+
return true;
|
|
294
|
+
return !metadata?.liveRecognitionStrategy;
|
|
295
|
+
};
|
|
296
|
+
const getCapturePageRecognitionText = (metadata) => `${metadata?.liveRecognitionReason ?? ''} ${metadata?.liveRecognitionPath ?? ''}`
|
|
297
|
+
.trim()
|
|
298
|
+
.toLowerCase();
|
|
299
|
+
const hasTrustedMrzCaptureEvidence = (metadata) => {
|
|
300
|
+
if (!metadata)
|
|
301
|
+
return false;
|
|
302
|
+
if (metadata.liveMrzCheckDigitsValid === false || metadata.liveMrzValidated === false) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
if (metadata.liveBoundsReliable === false && metadata.liveMrzValidated !== true)
|
|
306
|
+
return false;
|
|
307
|
+
if ((metadata.liveMrzConfidence ?? 0) < CAPTURE_CANDIDATE_MRZ_CONFIRMED_CONFIDENCE_MIN) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
if (metadata.liveMrzValidated === true)
|
|
311
|
+
return true;
|
|
312
|
+
if (metadata.liveMrzCheckDigitsValid === true)
|
|
313
|
+
return false;
|
|
314
|
+
const text = getCapturePageRecognitionText(metadata);
|
|
315
|
+
return metadata.liveRecognitionAccepted === true && text.includes('mrz');
|
|
316
|
+
};
|
|
317
|
+
const hasTrustedCardFrontRecognitionReason = (metadata) => {
|
|
318
|
+
if (!isCardFrontCaptureContext(metadata))
|
|
319
|
+
return true;
|
|
320
|
+
const reason = normalizeEvidenceKey(metadata?.liveRecognitionReason);
|
|
321
|
+
if (reason === 'quad' || reason === 'classifier')
|
|
322
|
+
return true;
|
|
323
|
+
if (isFiniteNumber(metadata?.classifierConfidence) &&
|
|
324
|
+
metadata.classifierConfidence >= CAPTURE_CANDIDATE_HIGH_CLASSIFIER_CONFIDENCE) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
return hasTrustedMrzCaptureEvidence(metadata);
|
|
328
|
+
};
|
|
329
|
+
const hasReadyCaptureCandidateDecision = (metadata) => metadata?.captureCandidateDecision === 'review_candidate' ||
|
|
330
|
+
metadata?.captureCandidateDecision === 'manual_ready' ||
|
|
331
|
+
metadata?.captureCandidateDecision === 'auto_ready';
|
|
332
|
+
const hasReadySafeCardLiveEvidence = (metadata) => {
|
|
333
|
+
if (!metadata)
|
|
334
|
+
return false;
|
|
335
|
+
if (metadata.cropShapeFamily !== 'card-like')
|
|
336
|
+
return false;
|
|
337
|
+
const hasReadyCandidateDecision = hasReadyCaptureCandidateDecision(metadata);
|
|
338
|
+
const hasAcceptedLiveRecognition = metadata.liveRecognitionAccepted === true;
|
|
339
|
+
if (!hasReadyCandidateDecision && !hasAcceptedLiveRecognition)
|
|
340
|
+
return false;
|
|
341
|
+
if (!hasTrustedCardFrontRecognitionReason(metadata))
|
|
342
|
+
return false;
|
|
343
|
+
return hasMarginSafeReliableCaptureQuad(metadata);
|
|
344
|
+
};
|
|
345
|
+
const hasHardCaptureCutOffEvidence = (metadata) => Boolean(metadata?.clamped === true ||
|
|
346
|
+
metadata?.unsafeReason === 'clamped_edge' ||
|
|
347
|
+
hasUnsafeInsetReliableCaptureQuad(metadata));
|
|
348
|
+
const isNativeViewfinderCaptureReason = (reason) => {
|
|
349
|
+
const normalized = String(reason || '').toLowerCase();
|
|
350
|
+
return (normalized === 'web_native_viewfinder_capture' ||
|
|
351
|
+
normalized.startsWith('web_native_viewfinder_capture_'));
|
|
352
|
+
};
|
|
353
|
+
const isNativeOverfullReason = (reason) => {
|
|
354
|
+
const normalized = String(reason || '').toLowerCase();
|
|
355
|
+
return (isNativeViewfinderCaptureReason(normalized) &&
|
|
356
|
+
(normalized.includes('too_large') ||
|
|
357
|
+
normalized.includes('nearly_full_frame') ||
|
|
358
|
+
normalized.includes('overfull') ||
|
|
359
|
+
normalized.includes('full_frame')));
|
|
360
|
+
};
|
|
361
|
+
const hasHighAreaNativeViewfinderEvidence = (metadata) => Boolean(isNativeViewfinderCaptureReason(metadata?.reason) &&
|
|
362
|
+
isFiniteNumber(metadata?.areaRatio) &&
|
|
363
|
+
metadata.areaRatio >= NATIVE_OVERFULL_AREA_RATIO_MIN);
|
|
364
|
+
const hasNativeOverfullCaptureRisk = (metadata) => Boolean(isNativeViewfinderCaptureReason(metadata?.reason) &&
|
|
365
|
+
(metadata?.unsafeReason === 'too_large' ||
|
|
366
|
+
metadata?.unsafeReason === 'nearly_full_frame' ||
|
|
367
|
+
isNativeOverfullReason(metadata?.reason) ||
|
|
368
|
+
hasHighAreaNativeViewfinderEvidence(metadata)));
|
|
369
|
+
const hasNativeClampedCaptureRisk = (metadata) => Boolean(isNativeViewfinderCaptureReason(metadata?.reason) &&
|
|
370
|
+
(metadata?.clamped === true || metadata?.unsafeReason === 'clamped_edge'));
|
|
371
|
+
const canTrustReadySafeCardEvidenceOverNativeUnsafeRisk = (metadata) => (hasReadySafeCardLiveEvidence(metadata) &&
|
|
372
|
+
!hasHardCaptureCutOffEvidence(metadata) &&
|
|
373
|
+
hasNativeOverfullCaptureRisk(metadata)) ||
|
|
374
|
+
(hasReadySafeCardLiveEvidence(metadata) &&
|
|
375
|
+
hasNativeClampedCaptureRisk(metadata) &&
|
|
376
|
+
!hasUnsafeInsetReliableCaptureQuad(metadata));
|
|
377
|
+
const hasReadySafeCardCropEvidence = (metadata) => !hasCaptureDocumentCutOffEvidence(metadata) &&
|
|
378
|
+
!hasHardCaptureCutOffEvidence(metadata) &&
|
|
379
|
+
isDocumentCropGeometrySafe(metadata) &&
|
|
380
|
+
hasReadySafeCardLiveEvidence(metadata) &&
|
|
381
|
+
(metadata.cutOffRisk !== 'high' || canTrustReadySafeCardEvidenceOverNativeUnsafeRisk(metadata));
|
|
382
|
+
export const isDocumentCropVerified = (metadata) => metadata.applied === true &&
|
|
383
|
+
((metadata.cropVerified === true &&
|
|
384
|
+
!isMeasuredCardViewfinderCrop(metadata) &&
|
|
385
|
+
!isReviewAutoCrop(metadata)) ||
|
|
386
|
+
(metadata.cornersOk === true && !isReviewAutoCrop(metadata)) ||
|
|
387
|
+
hasReadySafeCardCropEvidence(metadata) ||
|
|
388
|
+
(metadata.cornersOk !== false && hasMarginSafeDocumentCropEvidence(metadata)));
|
|
389
|
+
const isSafeMeasuredNativeCrop = (metadata) => isNativeViewfinderCrop(metadata) &&
|
|
390
|
+
isDocumentCropVerified(metadata) &&
|
|
391
|
+
isDocumentCropGeometrySafe(metadata);
|
|
392
|
+
export const getDocumentCropRejectedReason = (metadata) => {
|
|
393
|
+
if (hasQuietZoneClampCutOffEvidence(metadata))
|
|
394
|
+
return 'quiet_zone_clamped';
|
|
395
|
+
if (metadata.unsafeReason)
|
|
396
|
+
return metadata.unsafeReason;
|
|
397
|
+
if (metadata.clamped)
|
|
398
|
+
return 'clamped_edge';
|
|
399
|
+
const safetyInputs = getCropSafetyInputs(metadata);
|
|
400
|
+
if (!safetyInputs)
|
|
401
|
+
return 'unsafe_crop_rejected';
|
|
402
|
+
const { widthRatio, heightRatio, leftMarginRatio, topMarginRatio, rightMarginRatio, bottomMarginRatio, areaRatio, aspectRatio, shapeFamily, } = safetyInputs;
|
|
403
|
+
const safetyThresholds = getDocumentCropSafetyThresholds(aspectRatio, shapeFamily);
|
|
404
|
+
const minAreaRatio = getCropMinAreaRatio(metadata, aspectRatio, shapeFamily);
|
|
405
|
+
if (!isFiniteNumber(aspectRatio) ||
|
|
406
|
+
widthRatio < safetyThresholds.minWidthRatio ||
|
|
407
|
+
heightRatio < safetyThresholds.minHeightRatio ||
|
|
408
|
+
!isDocumentCropAspectSafe(aspectRatio, safetyThresholds)) {
|
|
409
|
+
return 'crop_too_narrow';
|
|
410
|
+
}
|
|
411
|
+
if (leftMarginRatio < 0 ||
|
|
412
|
+
rightMarginRatio < 0 ||
|
|
413
|
+
topMarginRatio < 0 ||
|
|
414
|
+
bottomMarginRatio < 0 ||
|
|
415
|
+
leftMarginRatio > safetyThresholds.maxHorizontalOutsideMarginRatio ||
|
|
416
|
+
rightMarginRatio > safetyThresholds.maxHorizontalOutsideMarginRatio ||
|
|
417
|
+
topMarginRatio > safetyThresholds.maxVerticalOutsideMarginRatio ||
|
|
418
|
+
bottomMarginRatio > safetyThresholds.maxVerticalOutsideMarginRatio) {
|
|
419
|
+
return 'unsafe_crop_rejected';
|
|
420
|
+
}
|
|
421
|
+
if (areaRatio < minAreaRatio)
|
|
422
|
+
return 'crop_too_small';
|
|
423
|
+
return 'unsafe_crop_rejected';
|
|
424
|
+
};
|
|
425
|
+
const getCropDefaultConfidenceThreshold = (cropMetadata) => cropMetadata.method === 'perspective'
|
|
426
|
+
? DEFAULT_CROP_CONFIDENCE_THRESHOLD
|
|
427
|
+
: DEFAULT_RECTANGLE_CROP_CONFIDENCE_THRESHOLD;
|
|
428
|
+
const buildCropArbitrationAssessment = ({ decision, policy, score, reason, reviewSafe, defaultSafe = false, cropVerified = false, cropSelectable = false, }) => ({
|
|
429
|
+
decision,
|
|
430
|
+
policy,
|
|
431
|
+
score,
|
|
432
|
+
reason,
|
|
433
|
+
reviewSafe,
|
|
434
|
+
defaultSafe,
|
|
435
|
+
cropVerified,
|
|
436
|
+
cropSelectable,
|
|
437
|
+
});
|
|
438
|
+
const hasDefaultableCropOutputDetail = (cropMetadata) => {
|
|
439
|
+
const size = cropMetadata.outputImageSize;
|
|
440
|
+
if (!size)
|
|
441
|
+
return true;
|
|
442
|
+
const trustedAnchoredCrop = isTrustedLiveBoundsCrop(cropMetadata) || isMrzAnchoredPassportDataPageCrop(cropMetadata);
|
|
443
|
+
const minLongSide = trustedAnchoredCrop
|
|
444
|
+
? TRUSTED_LIVE_BOUNDS_CROP_OUTPUT_MIN_LONG_SIDE
|
|
445
|
+
: DEFAULT_CROP_OUTPUT_MIN_LONG_SIDE;
|
|
446
|
+
const minShortSide = trustedAnchoredCrop
|
|
447
|
+
? TRUSTED_LIVE_BOUNDS_CROP_OUTPUT_MIN_SHORT_SIDE
|
|
448
|
+
: DEFAULT_CROP_OUTPUT_MIN_SHORT_SIDE;
|
|
449
|
+
return (Math.max(size.width, size.height) >= minLongSide &&
|
|
450
|
+
Math.min(size.width, size.height) >= minShortSide);
|
|
451
|
+
};
|
|
452
|
+
const hasDefaultSubmitInsetEvidence = (metadata) => {
|
|
453
|
+
const minimumInsetRatio = getMinimumCropBoundsInsetRatio(metadata);
|
|
454
|
+
return (minimumInsetRatio !== null && minimumInsetRatio >= DEFAULT_SUBMIT_CROP_VISIBLE_EDGE_INSET_RATIO);
|
|
455
|
+
};
|
|
456
|
+
export function assessDocumentCropCandidate(croppedBase64, cropMetadata) {
|
|
457
|
+
if (!cropMetadata || !croppedBase64 || !cropMetadata.applied) {
|
|
458
|
+
return buildCropArbitrationAssessment({
|
|
459
|
+
decision: 'unsafe_crop',
|
|
460
|
+
policy: 'disabled_unsafe',
|
|
461
|
+
score: 0,
|
|
462
|
+
reason: !cropMetadata ? 'missing_crop_metadata' : 'crop_not_applied',
|
|
463
|
+
reviewSafe: false,
|
|
464
|
+
defaultSafe: false,
|
|
465
|
+
cropVerified: false,
|
|
466
|
+
cropSelectable: false,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (hasCaptureDocumentCutOffEvidence(cropMetadata) ||
|
|
470
|
+
hasQuietZoneClampCutOffEvidence(cropMetadata)) {
|
|
471
|
+
return buildCropArbitrationAssessment({
|
|
472
|
+
decision: 'unsafe_crop',
|
|
473
|
+
policy: 'disabled_unsafe',
|
|
474
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 20)),
|
|
475
|
+
reason: hasQuietZoneClampCutOffEvidence(cropMetadata)
|
|
476
|
+
? 'quiet_zone_clamped'
|
|
477
|
+
: cropMetadata.reason || 'explicit_cutoff_evidence',
|
|
478
|
+
reviewSafe: false,
|
|
479
|
+
defaultSafe: false,
|
|
480
|
+
cropVerified: false,
|
|
481
|
+
cropSelectable: false,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (hasUnsafeLiveQuadCropEvidence(cropMetadata)) {
|
|
485
|
+
return buildCropArbitrationAssessment({
|
|
486
|
+
decision: 'unsafe_crop',
|
|
487
|
+
policy: 'disabled_unsafe',
|
|
488
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 25)),
|
|
489
|
+
reason: 'edge_tight_live_quad',
|
|
490
|
+
reviewSafe: false,
|
|
491
|
+
defaultSafe: false,
|
|
492
|
+
cropVerified: false,
|
|
493
|
+
cropSelectable: false,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
if (cropMetadata.cropSelectable === false && !isNativeViewfinderCrop(cropMetadata)) {
|
|
497
|
+
return buildCropArbitrationAssessment({
|
|
498
|
+
decision: 'unsafe_crop',
|
|
499
|
+
policy: 'disabled_unsafe',
|
|
500
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 35)),
|
|
501
|
+
reason: 'crop_marked_unselectable',
|
|
502
|
+
reviewSafe: false,
|
|
503
|
+
defaultSafe: false,
|
|
504
|
+
cropVerified: false,
|
|
505
|
+
cropSelectable: false,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (!hasDocumentCropGeometry(cropMetadata)) {
|
|
509
|
+
return buildCropArbitrationAssessment({
|
|
510
|
+
decision: 'original_only',
|
|
511
|
+
policy: 'original_only',
|
|
512
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 45)),
|
|
513
|
+
reason: 'missing_crop_geometry',
|
|
514
|
+
reviewSafe: false,
|
|
515
|
+
defaultSafe: false,
|
|
516
|
+
cropVerified: false,
|
|
517
|
+
cropSelectable: false,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (!isDocumentCropGeometrySafe(cropMetadata)) {
|
|
521
|
+
return buildCropArbitrationAssessment({
|
|
522
|
+
decision: 'unsafe_crop',
|
|
523
|
+
policy: 'disabled_unsafe',
|
|
524
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 35)),
|
|
525
|
+
reason: getDocumentCropRejectedReason(cropMetadata),
|
|
526
|
+
reviewSafe: false,
|
|
527
|
+
defaultSafe: false,
|
|
528
|
+
cropVerified: false,
|
|
529
|
+
cropSelectable: false,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
const hasTrustedCornerGeometry = isDocumentCropVerified(cropMetadata) || isSafeMeasuredNativeCrop(cropMetadata);
|
|
533
|
+
if (cropMetadata.cornersOk === false) {
|
|
534
|
+
return buildCropArbitrationAssessment({
|
|
535
|
+
decision: 'original_with_overlay',
|
|
536
|
+
policy: 'original_only',
|
|
537
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 55)),
|
|
538
|
+
reason: 'corners_not_verified',
|
|
539
|
+
reviewSafe: true,
|
|
540
|
+
defaultSafe: false,
|
|
541
|
+
cropVerified: false,
|
|
542
|
+
cropSelectable: false,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
if (!hasDefaultableCropOutputDetail(cropMetadata)) {
|
|
546
|
+
return buildCropArbitrationAssessment({
|
|
547
|
+
decision: 'original_with_overlay',
|
|
548
|
+
policy: 'original_only',
|
|
549
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 65)),
|
|
550
|
+
reason: 'crop_output_too_small',
|
|
551
|
+
reviewSafe: true,
|
|
552
|
+
defaultSafe: false,
|
|
553
|
+
cropVerified: hasTrustedCornerGeometry,
|
|
554
|
+
cropSelectable: false,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (DEFAULT_SUBMIT_CROP_INSET_FLOOR_ENABLED &&
|
|
558
|
+
hasTrustedCornerGeometry &&
|
|
559
|
+
!hasDefaultSubmitInsetEvidence(cropMetadata)) {
|
|
560
|
+
return buildCropArbitrationAssessment({
|
|
561
|
+
decision: 'original_with_overlay',
|
|
562
|
+
policy: 'original_only',
|
|
563
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 65)),
|
|
564
|
+
reason: 'crop_inset_too_small',
|
|
565
|
+
reviewSafe: true,
|
|
566
|
+
defaultSafe: false,
|
|
567
|
+
cropVerified: hasTrustedCornerGeometry,
|
|
568
|
+
cropSelectable: false,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (isSafeMeasuredNativeCrop(cropMetadata)) {
|
|
572
|
+
return buildCropArbitrationAssessment({
|
|
573
|
+
decision: 'default_crop',
|
|
574
|
+
policy: 'default',
|
|
575
|
+
score: Math.max(80, Math.round((cropMetadata.confidence ?? 0.55) * 100)),
|
|
576
|
+
reason: 'safe_measured_native_crop',
|
|
577
|
+
reviewSafe: true,
|
|
578
|
+
defaultSafe: true,
|
|
579
|
+
cropVerified: true,
|
|
580
|
+
cropSelectable: true,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
if ((cropMetadata.confidence ?? 0) >= getCropDefaultConfidenceThreshold(cropMetadata) &&
|
|
584
|
+
hasTrustedCornerGeometry) {
|
|
585
|
+
return buildCropArbitrationAssessment({
|
|
586
|
+
decision: 'default_crop',
|
|
587
|
+
policy: 'default',
|
|
588
|
+
score: Math.max(80, Math.round((cropMetadata.confidence ?? 0) * 100)),
|
|
589
|
+
reason: 'safe_verified_crop',
|
|
590
|
+
reviewSafe: true,
|
|
591
|
+
defaultSafe: true,
|
|
592
|
+
cropVerified: true,
|
|
593
|
+
cropSelectable: true,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return buildCropArbitrationAssessment({
|
|
597
|
+
decision: 'original_with_overlay',
|
|
598
|
+
policy: 'original_only',
|
|
599
|
+
score: Math.max(0, Math.round((cropMetadata.confidence ?? 0) * 70)),
|
|
600
|
+
reason: hasTrustedCornerGeometry ? 'crop_confidence_too_low' : 'crop_not_verified',
|
|
601
|
+
reviewSafe: true,
|
|
602
|
+
defaultSafe: false,
|
|
603
|
+
cropVerified: hasTrustedCornerGeometry,
|
|
604
|
+
cropSelectable: false,
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
export function getDocumentCropReviewPolicy(croppedBase64, cropMetadata) {
|
|
608
|
+
return assessDocumentCropCandidate(croppedBase64, cropMetadata).policy;
|
|
609
|
+
}
|
|
610
|
+
export function getDocumentCropTrustTier(croppedBase64, cropMetadata, client = 'web') {
|
|
611
|
+
const assessment = assessDocumentCropCandidate(croppedBase64, cropMetadata);
|
|
612
|
+
return decideCaptureTrust({
|
|
613
|
+
context: {
|
|
614
|
+
client,
|
|
615
|
+
phase: 'capture',
|
|
616
|
+
captureSource: cropMetadata?.cropSource === 'native_platform_scanner'
|
|
617
|
+
? 'native_platform_scanner'
|
|
618
|
+
: 'camera',
|
|
619
|
+
},
|
|
620
|
+
capture: {
|
|
621
|
+
hasMetrics: true,
|
|
622
|
+
documentCandidate: true,
|
|
623
|
+
reviewSafe: assessment.reviewSafe,
|
|
624
|
+
autoSafe: false,
|
|
625
|
+
},
|
|
626
|
+
crop: {
|
|
627
|
+
applied: cropMetadata?.applied,
|
|
628
|
+
provenance: cropMetadata?.cropProvenance ?? cropMetadata?.cropSource ?? null,
|
|
629
|
+
unsafe: assessment.decision === 'unsafe_crop' || assessment.policy === 'disabled_unsafe',
|
|
630
|
+
unsafeReason: cropMetadata?.unsafeReason ?? undefined,
|
|
631
|
+
clamped: cropMetadata?.clamped,
|
|
632
|
+
cutOffRisk: cropMetadata?.cutOffRisk,
|
|
633
|
+
aspectSafe: assessment.reason === 'crop_bad_aspect' ? false : undefined,
|
|
634
|
+
cornersOk: cropMetadata?.cornersOk,
|
|
635
|
+
cropVerified: assessment.cropVerified,
|
|
636
|
+
cropSelectable: assessment.cropSelectable,
|
|
637
|
+
footprintTrusted: assessment.defaultSafe,
|
|
638
|
+
renderedContentOk: assessment.defaultSafe || assessment.reviewSafe,
|
|
639
|
+
},
|
|
640
|
+
}).cropTrustTier;
|
|
641
|
+
}
|
|
642
|
+
//# sourceMappingURL=document-crop-arbitration.js.map
|