@stacksjs/ts-cloud 0.1.8 → 0.1.9
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/bin/cli.js +1 -1
- package/package.json +18 -16
- package/src/aws/acm.ts +768 -0
- package/src/aws/application-autoscaling.ts +845 -0
- package/src/aws/bedrock.ts +4074 -0
- package/src/aws/client.ts +891 -0
- package/src/aws/cloudformation.ts +896 -0
- package/src/aws/cloudfront.ts +1531 -0
- package/src/aws/cloudwatch-logs.ts +154 -0
- package/src/aws/comprehend.ts +839 -0
- package/src/aws/connect.ts +1056 -0
- package/src/aws/deploy-imap.ts +384 -0
- package/src/aws/dynamodb.ts +340 -0
- package/src/aws/ec2.ts +1385 -0
- package/src/aws/ecr.ts +621 -0
- package/src/aws/ecs.ts +615 -0
- package/src/aws/elasticache.ts +301 -0
- package/src/aws/elbv2.ts +942 -0
- package/src/aws/email.ts +928 -0
- package/src/aws/eventbridge.ts +248 -0
- package/src/aws/iam.ts +1689 -0
- package/src/aws/imap-server.ts +2100 -0
- package/src/aws/index.ts +213 -0
- package/src/aws/kendra.ts +1097 -0
- package/src/aws/lambda.ts +786 -0
- package/src/aws/opensearch.ts +158 -0
- package/src/aws/personalize.ts +977 -0
- package/src/aws/polly.ts +559 -0
- package/src/aws/rds.ts +888 -0
- package/src/aws/rekognition.ts +846 -0
- package/src/aws/route53-domains.ts +359 -0
- package/src/aws/route53.ts +1046 -0
- package/src/aws/s3.ts +2334 -0
- package/src/aws/scheduler.ts +571 -0
- package/src/aws/secrets-manager.ts +769 -0
- package/src/aws/ses.ts +1081 -0
- package/src/aws/setup-phone.ts +104 -0
- package/src/aws/setup-sms.ts +580 -0
- package/src/aws/sms.ts +1735 -0
- package/src/aws/smtp-server.ts +531 -0
- package/src/aws/sns.ts +758 -0
- package/src/aws/sqs.ts +382 -0
- package/src/aws/ssm.ts +807 -0
- package/src/aws/sts.ts +92 -0
- package/src/aws/support.ts +391 -0
- package/src/aws/test-imap.ts +86 -0
- package/src/aws/textract.ts +780 -0
- package/src/aws/transcribe.ts +108 -0
- package/src/aws/translate.ts +641 -0
- package/src/aws/voice.ts +1379 -0
- package/src/config.ts +35 -0
- package/src/deploy/index.ts +7 -0
- package/src/deploy/static-site-external-dns.ts +945 -0
- package/src/deploy/static-site.ts +1175 -0
- package/src/dns/cloudflare.ts +548 -0
- package/src/dns/godaddy.ts +412 -0
- package/src/dns/index.ts +205 -0
- package/src/dns/porkbun.ts +362 -0
- package/src/dns/route53-adapter.ts +414 -0
- package/src/dns/types.ts +119 -0
- package/src/dns/validator.ts +369 -0
- package/src/generators/index.ts +5 -0
- package/src/generators/infrastructure.ts +1660 -0
- package/src/index.ts +163 -0
- package/src/push/apns.ts +452 -0
- package/src/push/fcm.ts +506 -0
- package/src/push/index.ts +58 -0
- package/src/security/pre-deploy-scanner.ts +655 -0
- package/src/ssl/acme-client.ts +478 -0
- package/src/ssl/index.ts +7 -0
- package/src/ssl/letsencrypt.ts +747 -0
- package/src/types.ts +2 -0
- package/src/utils/cli.ts +398 -0
- package/src/validation/index.ts +5 -0
- package/src/validation/template.ts +405 -0
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Rekognition Client
|
|
3
|
+
* Image and video analysis - face detection, object detection, celebrity recognition
|
|
4
|
+
* No external SDK dependencies - implements AWS Signature V4 directly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { AWSClient } from './client'
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export interface S3Object {
|
|
14
|
+
Bucket?: string
|
|
15
|
+
Name?: string
|
|
16
|
+
Version?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Image {
|
|
20
|
+
Bytes?: Uint8Array
|
|
21
|
+
S3Object?: S3Object
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Video {
|
|
25
|
+
S3Object?: S3Object
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface BoundingBox {
|
|
29
|
+
Width?: number
|
|
30
|
+
Height?: number
|
|
31
|
+
Left?: number
|
|
32
|
+
Top?: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Landmark {
|
|
36
|
+
Type?: string
|
|
37
|
+
X?: number
|
|
38
|
+
Y?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Pose {
|
|
42
|
+
Roll?: number
|
|
43
|
+
Yaw?: number
|
|
44
|
+
Pitch?: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ImageQuality {
|
|
48
|
+
Brightness?: number
|
|
49
|
+
Sharpness?: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface Emotion {
|
|
53
|
+
Type?: 'HAPPY' | 'SAD' | 'ANGRY' | 'CONFUSED' | 'DISGUSTED' | 'SURPRISED' | 'CALM' | 'UNKNOWN' | 'FEAR'
|
|
54
|
+
Confidence?: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface FaceDetail {
|
|
58
|
+
BoundingBox?: BoundingBox
|
|
59
|
+
AgeRange?: { Low?: number; High?: number }
|
|
60
|
+
Smile?: { Value?: boolean; Confidence?: number }
|
|
61
|
+
Eyeglasses?: { Value?: boolean; Confidence?: number }
|
|
62
|
+
Sunglasses?: { Value?: boolean; Confidence?: number }
|
|
63
|
+
Gender?: { Value?: 'Male' | 'Female'; Confidence?: number }
|
|
64
|
+
Beard?: { Value?: boolean; Confidence?: number }
|
|
65
|
+
Mustache?: { Value?: boolean; Confidence?: number }
|
|
66
|
+
EyesOpen?: { Value?: boolean; Confidence?: number }
|
|
67
|
+
MouthOpen?: { Value?: boolean; Confidence?: number }
|
|
68
|
+
Emotions?: Emotion[]
|
|
69
|
+
Landmarks?: Landmark[]
|
|
70
|
+
Pose?: Pose
|
|
71
|
+
Quality?: ImageQuality
|
|
72
|
+
Confidence?: number
|
|
73
|
+
FaceOccluded?: { Value?: boolean; Confidence?: number }
|
|
74
|
+
EyeDirection?: { Yaw?: number; Pitch?: number; Confidence?: number }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DetectFacesCommandInput {
|
|
78
|
+
Image: Image
|
|
79
|
+
Attributes?: ('DEFAULT' | 'ALL' | 'AGE_RANGE' | 'BEARD' | 'EMOTIONS' | 'EYE_DIRECTION' | 'EYEGLASSES' | 'EYES_OPEN' | 'FACE_OCCLUDED' | 'GENDER' | 'MOUTH_OPEN' | 'MUSTACHE' | 'POSE' | 'QUALITY' | 'SMILE' | 'SUNGLASSES')[]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DetectFacesCommandOutput {
|
|
83
|
+
FaceDetails?: FaceDetail[]
|
|
84
|
+
OrientationCorrection?: 'ROTATE_0' | 'ROTATE_90' | 'ROTATE_180' | 'ROTATE_270'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface Label {
|
|
88
|
+
Name?: string
|
|
89
|
+
Confidence?: number
|
|
90
|
+
Instances?: Array<{
|
|
91
|
+
BoundingBox?: BoundingBox
|
|
92
|
+
Confidence?: number
|
|
93
|
+
DominantColors?: Array<{
|
|
94
|
+
Red?: number
|
|
95
|
+
Green?: number
|
|
96
|
+
Blue?: number
|
|
97
|
+
HexCode?: string
|
|
98
|
+
SimplifiedColor?: string
|
|
99
|
+
CSSColor?: string
|
|
100
|
+
PixelPercent?: number
|
|
101
|
+
}>
|
|
102
|
+
}>
|
|
103
|
+
Parents?: Array<{ Name?: string }>
|
|
104
|
+
Aliases?: Array<{ Name?: string }>
|
|
105
|
+
Categories?: Array<{ Name?: string }>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface DetectLabelsCommandInput {
|
|
109
|
+
Image: Image
|
|
110
|
+
MaxLabels?: number
|
|
111
|
+
MinConfidence?: number
|
|
112
|
+
Features?: ('GENERAL_LABELS' | 'IMAGE_PROPERTIES')[]
|
|
113
|
+
Settings?: {
|
|
114
|
+
GeneralLabels?: {
|
|
115
|
+
LabelInclusionFilters?: string[]
|
|
116
|
+
LabelExclusionFilters?: string[]
|
|
117
|
+
LabelCategoryInclusionFilters?: string[]
|
|
118
|
+
LabelCategoryExclusionFilters?: string[]
|
|
119
|
+
}
|
|
120
|
+
ImageProperties?: {
|
|
121
|
+
MaxDominantColors?: number
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface DetectLabelsCommandOutput {
|
|
127
|
+
Labels?: Label[]
|
|
128
|
+
OrientationCorrection?: string
|
|
129
|
+
LabelModelVersion?: string
|
|
130
|
+
ImageProperties?: {
|
|
131
|
+
Quality?: {
|
|
132
|
+
Brightness?: number
|
|
133
|
+
Sharpness?: number
|
|
134
|
+
Contrast?: number
|
|
135
|
+
}
|
|
136
|
+
DominantColors?: Array<{
|
|
137
|
+
Red?: number
|
|
138
|
+
Green?: number
|
|
139
|
+
Blue?: number
|
|
140
|
+
HexCode?: string
|
|
141
|
+
SimplifiedColor?: string
|
|
142
|
+
CSSColor?: string
|
|
143
|
+
PixelPercent?: number
|
|
144
|
+
}>
|
|
145
|
+
Foreground?: {
|
|
146
|
+
Quality?: { Brightness?: number; Sharpness?: number }
|
|
147
|
+
DominantColors?: Array<{
|
|
148
|
+
Red?: number
|
|
149
|
+
Green?: number
|
|
150
|
+
Blue?: number
|
|
151
|
+
HexCode?: string
|
|
152
|
+
CSSColor?: string
|
|
153
|
+
PixelPercent?: number
|
|
154
|
+
}>
|
|
155
|
+
}
|
|
156
|
+
Background?: {
|
|
157
|
+
Quality?: { Brightness?: number; Sharpness?: number }
|
|
158
|
+
DominantColors?: Array<{
|
|
159
|
+
Red?: number
|
|
160
|
+
Green?: number
|
|
161
|
+
Blue?: number
|
|
162
|
+
HexCode?: string
|
|
163
|
+
CSSColor?: string
|
|
164
|
+
PixelPercent?: number
|
|
165
|
+
}>
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface DetectTextCommandInput {
|
|
171
|
+
Image: Image
|
|
172
|
+
Filters?: {
|
|
173
|
+
WordFilter?: {
|
|
174
|
+
MinConfidence?: number
|
|
175
|
+
MinBoundingBoxHeight?: number
|
|
176
|
+
MinBoundingBoxWidth?: number
|
|
177
|
+
}
|
|
178
|
+
RegionsOfInterest?: Array<{
|
|
179
|
+
BoundingBox?: BoundingBox
|
|
180
|
+
Polygon?: Array<{ X?: number; Y?: number }>
|
|
181
|
+
}>
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface TextDetection {
|
|
186
|
+
DetectedText?: string
|
|
187
|
+
Type?: 'LINE' | 'WORD'
|
|
188
|
+
Id?: number
|
|
189
|
+
ParentId?: number
|
|
190
|
+
Confidence?: number
|
|
191
|
+
Geometry?: {
|
|
192
|
+
BoundingBox?: BoundingBox
|
|
193
|
+
Polygon?: Array<{ X?: number; Y?: number }>
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface DetectTextCommandOutput {
|
|
198
|
+
TextDetections?: TextDetection[]
|
|
199
|
+
TextModelVersion?: string
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface DetectModerationLabelsCommandInput {
|
|
203
|
+
Image: Image
|
|
204
|
+
MinConfidence?: number
|
|
205
|
+
HumanLoopConfig?: {
|
|
206
|
+
HumanLoopName: string
|
|
207
|
+
FlowDefinitionArn: string
|
|
208
|
+
DataAttributes?: {
|
|
209
|
+
ContentClassifiers?: ('FreeOfPersonallyIdentifiableInformation' | 'FreeOfAdultContent')[]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
ProjectVersion?: string
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface ModerationLabel {
|
|
216
|
+
Confidence?: number
|
|
217
|
+
Name?: string
|
|
218
|
+
ParentName?: string
|
|
219
|
+
TaxonomyLevel?: number
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface DetectModerationLabelsCommandOutput {
|
|
223
|
+
ModerationLabels?: ModerationLabel[]
|
|
224
|
+
ModerationModelVersion?: string
|
|
225
|
+
HumanLoopActivationOutput?: {
|
|
226
|
+
HumanLoopArn?: string
|
|
227
|
+
HumanLoopActivationReasons?: string[]
|
|
228
|
+
HumanLoopActivationConditionsEvaluationResults?: string
|
|
229
|
+
}
|
|
230
|
+
ProjectVersion?: string
|
|
231
|
+
ContentTypes?: Array<{
|
|
232
|
+
Confidence?: number
|
|
233
|
+
Name?: string
|
|
234
|
+
}>
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface Celebrity {
|
|
238
|
+
Urls?: string[]
|
|
239
|
+
Name?: string
|
|
240
|
+
Id?: string
|
|
241
|
+
Face?: {
|
|
242
|
+
BoundingBox?: BoundingBox
|
|
243
|
+
Confidence?: number
|
|
244
|
+
Landmarks?: Landmark[]
|
|
245
|
+
Pose?: Pose
|
|
246
|
+
Quality?: ImageQuality
|
|
247
|
+
Emotions?: Emotion[]
|
|
248
|
+
Smile?: { Value?: boolean; Confidence?: number }
|
|
249
|
+
}
|
|
250
|
+
MatchConfidence?: number
|
|
251
|
+
KnownGender?: { Type?: 'Male' | 'Female' | 'Nonbinary' | 'Unlisted' }
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface RecognizeCelebritiesCommandInput {
|
|
255
|
+
Image: Image
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface RecognizeCelebritiesCommandOutput {
|
|
259
|
+
CelebrityFaces?: Celebrity[]
|
|
260
|
+
UnrecognizedFaces?: FaceDetail[]
|
|
261
|
+
OrientationCorrection?: string
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface CompareFacesCommandInput {
|
|
265
|
+
SourceImage: Image
|
|
266
|
+
TargetImage: Image
|
|
267
|
+
SimilarityThreshold?: number
|
|
268
|
+
QualityFilter?: 'NONE' | 'AUTO' | 'LOW' | 'MEDIUM' | 'HIGH'
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface CompareFacesMatch {
|
|
272
|
+
Similarity?: number
|
|
273
|
+
Face?: {
|
|
274
|
+
BoundingBox?: BoundingBox
|
|
275
|
+
Confidence?: number
|
|
276
|
+
Landmarks?: Landmark[]
|
|
277
|
+
Pose?: Pose
|
|
278
|
+
Quality?: ImageQuality
|
|
279
|
+
Emotions?: Emotion[]
|
|
280
|
+
Smile?: { Value?: boolean; Confidence?: number }
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface CompareFacesCommandOutput {
|
|
285
|
+
SourceImageFace?: {
|
|
286
|
+
BoundingBox?: BoundingBox
|
|
287
|
+
Confidence?: number
|
|
288
|
+
}
|
|
289
|
+
FaceMatches?: CompareFacesMatch[]
|
|
290
|
+
UnmatchedFaces?: FaceDetail[]
|
|
291
|
+
SourceImageOrientationCorrection?: string
|
|
292
|
+
TargetImageOrientationCorrection?: string
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface CreateCollectionCommandInput {
|
|
296
|
+
CollectionId: string
|
|
297
|
+
Tags?: Record<string, string>
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export interface CreateCollectionCommandOutput {
|
|
301
|
+
StatusCode?: number
|
|
302
|
+
CollectionArn?: string
|
|
303
|
+
FaceModelVersion?: string
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface DeleteCollectionCommandInput {
|
|
307
|
+
CollectionId: string
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface DeleteCollectionCommandOutput {
|
|
311
|
+
StatusCode?: number
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface ListCollectionsCommandInput {
|
|
315
|
+
NextToken?: string
|
|
316
|
+
MaxResults?: number
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface ListCollectionsCommandOutput {
|
|
320
|
+
CollectionIds?: string[]
|
|
321
|
+
NextToken?: string
|
|
322
|
+
FaceModelVersions?: string[]
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export interface IndexFacesCommandInput {
|
|
326
|
+
CollectionId: string
|
|
327
|
+
Image: Image
|
|
328
|
+
ExternalImageId?: string
|
|
329
|
+
DetectionAttributes?: ('DEFAULT' | 'ALL')[]
|
|
330
|
+
MaxFaces?: number
|
|
331
|
+
QualityFilter?: 'NONE' | 'AUTO' | 'LOW' | 'MEDIUM' | 'HIGH'
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface FaceRecord {
|
|
335
|
+
Face?: {
|
|
336
|
+
FaceId?: string
|
|
337
|
+
BoundingBox?: BoundingBox
|
|
338
|
+
ImageId?: string
|
|
339
|
+
ExternalImageId?: string
|
|
340
|
+
Confidence?: number
|
|
341
|
+
IndexFacesModelVersion?: string
|
|
342
|
+
}
|
|
343
|
+
FaceDetail?: FaceDetail
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export interface IndexFacesCommandOutput {
|
|
347
|
+
FaceRecords?: FaceRecord[]
|
|
348
|
+
OrientationCorrection?: string
|
|
349
|
+
FaceModelVersion?: string
|
|
350
|
+
UnindexedFaces?: Array<{
|
|
351
|
+
Reasons?: ('EXCEEDS_MAX_FACES' | 'EXTREME_POSE' | 'LOW_BRIGHTNESS' | 'LOW_SHARPNESS' | 'LOW_CONFIDENCE' | 'SMALL_BOUNDING_BOX' | 'LOW_FACE_QUALITY')[]
|
|
352
|
+
FaceDetail?: FaceDetail
|
|
353
|
+
}>
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface SearchFacesByImageCommandInput {
|
|
357
|
+
CollectionId: string
|
|
358
|
+
Image: Image
|
|
359
|
+
MaxFaces?: number
|
|
360
|
+
FaceMatchThreshold?: number
|
|
361
|
+
QualityFilter?: 'NONE' | 'AUTO' | 'LOW' | 'MEDIUM' | 'HIGH'
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface FaceMatch {
|
|
365
|
+
Similarity?: number
|
|
366
|
+
Face?: {
|
|
367
|
+
FaceId?: string
|
|
368
|
+
BoundingBox?: BoundingBox
|
|
369
|
+
ImageId?: string
|
|
370
|
+
ExternalImageId?: string
|
|
371
|
+
Confidence?: number
|
|
372
|
+
IndexFacesModelVersion?: string
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface SearchFacesByImageCommandOutput {
|
|
377
|
+
SearchedFaceBoundingBox?: BoundingBox
|
|
378
|
+
SearchedFaceConfidence?: number
|
|
379
|
+
FaceMatches?: FaceMatch[]
|
|
380
|
+
FaceModelVersion?: string
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export interface SearchFacesCommandInput {
|
|
384
|
+
CollectionId: string
|
|
385
|
+
FaceId: string
|
|
386
|
+
MaxFaces?: number
|
|
387
|
+
FaceMatchThreshold?: number
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export interface SearchFacesCommandOutput {
|
|
391
|
+
SearchedFaceId?: string
|
|
392
|
+
FaceMatches?: FaceMatch[]
|
|
393
|
+
FaceModelVersion?: string
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export interface StartLabelDetectionCommandInput {
|
|
397
|
+
Video: Video
|
|
398
|
+
ClientRequestToken?: string
|
|
399
|
+
MinConfidence?: number
|
|
400
|
+
NotificationChannel?: {
|
|
401
|
+
SNSTopicArn: string
|
|
402
|
+
RoleArn: string
|
|
403
|
+
}
|
|
404
|
+
JobTag?: string
|
|
405
|
+
Features?: ('GENERAL_LABELS' | 'IMAGE_PROPERTIES')[]
|
|
406
|
+
Settings?: {
|
|
407
|
+
GeneralLabels?: {
|
|
408
|
+
LabelInclusionFilters?: string[]
|
|
409
|
+
LabelExclusionFilters?: string[]
|
|
410
|
+
LabelCategoryInclusionFilters?: string[]
|
|
411
|
+
LabelCategoryExclusionFilters?: string[]
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export interface StartLabelDetectionCommandOutput {
|
|
417
|
+
JobId?: string
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export interface GetLabelDetectionCommandInput {
|
|
421
|
+
JobId: string
|
|
422
|
+
MaxResults?: number
|
|
423
|
+
NextToken?: string
|
|
424
|
+
SortBy?: 'NAME' | 'TIMESTAMP'
|
|
425
|
+
AggregateBy?: 'TIMESTAMPS' | 'SEGMENTS'
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export interface LabelDetection {
|
|
429
|
+
Timestamp?: number
|
|
430
|
+
Label?: Label
|
|
431
|
+
StartTimestampMillis?: number
|
|
432
|
+
EndTimestampMillis?: number
|
|
433
|
+
DurationMillis?: number
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export interface GetLabelDetectionCommandOutput {
|
|
437
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED'
|
|
438
|
+
StatusMessage?: string
|
|
439
|
+
VideoMetadata?: {
|
|
440
|
+
Codec?: string
|
|
441
|
+
DurationMillis?: number
|
|
442
|
+
Format?: string
|
|
443
|
+
FrameRate?: number
|
|
444
|
+
FrameHeight?: number
|
|
445
|
+
FrameWidth?: number
|
|
446
|
+
ColorRange?: string
|
|
447
|
+
}
|
|
448
|
+
NextToken?: string
|
|
449
|
+
Labels?: LabelDetection[]
|
|
450
|
+
LabelModelVersion?: string
|
|
451
|
+
JobId?: string
|
|
452
|
+
Video?: { S3Object?: S3Object }
|
|
453
|
+
JobTag?: string
|
|
454
|
+
GetRequestMetadata?: {
|
|
455
|
+
SortBy?: string
|
|
456
|
+
AggregateBy?: string
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export interface StartFaceDetectionCommandInput {
|
|
461
|
+
Video: Video
|
|
462
|
+
ClientRequestToken?: string
|
|
463
|
+
NotificationChannel?: {
|
|
464
|
+
SNSTopicArn: string
|
|
465
|
+
RoleArn: string
|
|
466
|
+
}
|
|
467
|
+
FaceAttributes?: 'DEFAULT' | 'ALL'
|
|
468
|
+
JobTag?: string
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export interface StartFaceDetectionCommandOutput {
|
|
472
|
+
JobId?: string
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export interface GetFaceDetectionCommandInput {
|
|
476
|
+
JobId: string
|
|
477
|
+
MaxResults?: number
|
|
478
|
+
NextToken?: string
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export interface FaceDetection {
|
|
482
|
+
Timestamp?: number
|
|
483
|
+
Face?: FaceDetail
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export interface GetFaceDetectionCommandOutput {
|
|
487
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED'
|
|
488
|
+
StatusMessage?: string
|
|
489
|
+
VideoMetadata?: {
|
|
490
|
+
Codec?: string
|
|
491
|
+
DurationMillis?: number
|
|
492
|
+
Format?: string
|
|
493
|
+
FrameRate?: number
|
|
494
|
+
FrameHeight?: number
|
|
495
|
+
FrameWidth?: number
|
|
496
|
+
}
|
|
497
|
+
NextToken?: string
|
|
498
|
+
Faces?: FaceDetection[]
|
|
499
|
+
JobId?: string
|
|
500
|
+
Video?: { S3Object?: S3Object }
|
|
501
|
+
JobTag?: string
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export interface StartContentModerationCommandInput {
|
|
505
|
+
Video: Video
|
|
506
|
+
MinConfidence?: number
|
|
507
|
+
ClientRequestToken?: string
|
|
508
|
+
NotificationChannel?: {
|
|
509
|
+
SNSTopicArn: string
|
|
510
|
+
RoleArn: string
|
|
511
|
+
}
|
|
512
|
+
JobTag?: string
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export interface StartContentModerationCommandOutput {
|
|
516
|
+
JobId?: string
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export interface GetContentModerationCommandInput {
|
|
520
|
+
JobId: string
|
|
521
|
+
MaxResults?: number
|
|
522
|
+
NextToken?: string
|
|
523
|
+
SortBy?: 'NAME' | 'TIMESTAMP'
|
|
524
|
+
AggregateBy?: 'TIMESTAMPS' | 'SEGMENTS'
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export interface ContentModerationDetection {
|
|
528
|
+
Timestamp?: number
|
|
529
|
+
ModerationLabel?: ModerationLabel
|
|
530
|
+
StartTimestampMillis?: number
|
|
531
|
+
EndTimestampMillis?: number
|
|
532
|
+
DurationMillis?: number
|
|
533
|
+
ContentTypes?: Array<{
|
|
534
|
+
Confidence?: number
|
|
535
|
+
Name?: string
|
|
536
|
+
}>
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface GetContentModerationCommandOutput {
|
|
540
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED'
|
|
541
|
+
StatusMessage?: string
|
|
542
|
+
VideoMetadata?: {
|
|
543
|
+
Codec?: string
|
|
544
|
+
DurationMillis?: number
|
|
545
|
+
Format?: string
|
|
546
|
+
FrameRate?: number
|
|
547
|
+
FrameHeight?: number
|
|
548
|
+
FrameWidth?: number
|
|
549
|
+
}
|
|
550
|
+
ModerationLabels?: ContentModerationDetection[]
|
|
551
|
+
NextToken?: string
|
|
552
|
+
ModerationModelVersion?: string
|
|
553
|
+
JobId?: string
|
|
554
|
+
Video?: { S3Object?: S3Object }
|
|
555
|
+
JobTag?: string
|
|
556
|
+
GetRequestMetadata?: {
|
|
557
|
+
SortBy?: string
|
|
558
|
+
AggregateBy?: string
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// ============================================================================
|
|
563
|
+
// Rekognition Client
|
|
564
|
+
// ============================================================================
|
|
565
|
+
|
|
566
|
+
export class RekognitionClient {
|
|
567
|
+
private client: AWSClient
|
|
568
|
+
private region: string
|
|
569
|
+
|
|
570
|
+
constructor(region: string = 'us-east-1') {
|
|
571
|
+
this.region = region
|
|
572
|
+
this.client = new AWSClient()
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
private async request<T>(action: string, params: Record<string, unknown>): Promise<T> {
|
|
576
|
+
return this.client.request({
|
|
577
|
+
service: 'rekognition',
|
|
578
|
+
region: this.region,
|
|
579
|
+
method: 'POST',
|
|
580
|
+
path: '/',
|
|
581
|
+
headers: {
|
|
582
|
+
'Content-Type': 'application/x-amz-json-1.1',
|
|
583
|
+
'X-Amz-Target': `RekognitionService.${action}`,
|
|
584
|
+
},
|
|
585
|
+
body: JSON.stringify(params),
|
|
586
|
+
})
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// -------------------------------------------------------------------------
|
|
590
|
+
// Image Analysis
|
|
591
|
+
// -------------------------------------------------------------------------
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Detect faces in an image
|
|
595
|
+
*/
|
|
596
|
+
async detectFaces(params: DetectFacesCommandInput): Promise<DetectFacesCommandOutput> {
|
|
597
|
+
return this.request('DetectFaces', params as unknown as Record<string, unknown>)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Detect labels (objects, scenes, concepts) in an image
|
|
602
|
+
*/
|
|
603
|
+
async detectLabels(params: DetectLabelsCommandInput): Promise<DetectLabelsCommandOutput> {
|
|
604
|
+
return this.request('DetectLabels', params as unknown as Record<string, unknown>)
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Detect text in an image
|
|
609
|
+
*/
|
|
610
|
+
async detectText(params: DetectTextCommandInput): Promise<DetectTextCommandOutput> {
|
|
611
|
+
return this.request('DetectText', params as unknown as Record<string, unknown>)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Detect moderation labels (unsafe content)
|
|
616
|
+
*/
|
|
617
|
+
async detectModerationLabels(params: DetectModerationLabelsCommandInput): Promise<DetectModerationLabelsCommandOutput> {
|
|
618
|
+
return this.request('DetectModerationLabels', params as unknown as Record<string, unknown>)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Recognize celebrities in an image
|
|
623
|
+
*/
|
|
624
|
+
async recognizeCelebrities(params: RecognizeCelebritiesCommandInput): Promise<RecognizeCelebritiesCommandOutput> {
|
|
625
|
+
return this.request('RecognizeCelebrities', params as unknown as Record<string, unknown>)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Compare faces between source and target images
|
|
630
|
+
*/
|
|
631
|
+
async compareFaces(params: CompareFacesCommandInput): Promise<CompareFacesCommandOutput> {
|
|
632
|
+
return this.request('CompareFaces', params as unknown as Record<string, unknown>)
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// -------------------------------------------------------------------------
|
|
636
|
+
// Face Collection Management
|
|
637
|
+
// -------------------------------------------------------------------------
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Create a face collection
|
|
641
|
+
*/
|
|
642
|
+
async createCollection(params: CreateCollectionCommandInput): Promise<CreateCollectionCommandOutput> {
|
|
643
|
+
return this.request('CreateCollection', params as unknown as Record<string, unknown>)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Delete a face collection
|
|
648
|
+
*/
|
|
649
|
+
async deleteCollection(params: DeleteCollectionCommandInput): Promise<DeleteCollectionCommandOutput> {
|
|
650
|
+
return this.request('DeleteCollection', params as unknown as Record<string, unknown>)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* List face collections
|
|
655
|
+
*/
|
|
656
|
+
async listCollections(params?: ListCollectionsCommandInput): Promise<ListCollectionsCommandOutput> {
|
|
657
|
+
return this.request('ListCollections', (params || {}) as unknown as Record<string, unknown>)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Index faces from an image into a collection
|
|
662
|
+
*/
|
|
663
|
+
async indexFaces(params: IndexFacesCommandInput): Promise<IndexFacesCommandOutput> {
|
|
664
|
+
return this.request('IndexFaces', params as unknown as Record<string, unknown>)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Search for faces in a collection using an image
|
|
669
|
+
*/
|
|
670
|
+
async searchFacesByImage(params: SearchFacesByImageCommandInput): Promise<SearchFacesByImageCommandOutput> {
|
|
671
|
+
return this.request('SearchFacesByImage', params as unknown as Record<string, unknown>)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Search for faces in a collection using a face ID
|
|
676
|
+
*/
|
|
677
|
+
async searchFaces(params: SearchFacesCommandInput): Promise<SearchFacesCommandOutput> {
|
|
678
|
+
return this.request('SearchFaces', params as unknown as Record<string, unknown>)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// -------------------------------------------------------------------------
|
|
682
|
+
// Video Analysis
|
|
683
|
+
// -------------------------------------------------------------------------
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Start async label detection on video
|
|
687
|
+
*/
|
|
688
|
+
async startLabelDetection(params: StartLabelDetectionCommandInput): Promise<StartLabelDetectionCommandOutput> {
|
|
689
|
+
return this.request('StartLabelDetection', params as unknown as Record<string, unknown>)
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Get results of label detection job
|
|
694
|
+
*/
|
|
695
|
+
async getLabelDetection(params: GetLabelDetectionCommandInput): Promise<GetLabelDetectionCommandOutput> {
|
|
696
|
+
return this.request('GetLabelDetection', params as unknown as Record<string, unknown>)
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Start async face detection on video
|
|
701
|
+
*/
|
|
702
|
+
async startFaceDetection(params: StartFaceDetectionCommandInput): Promise<StartFaceDetectionCommandOutput> {
|
|
703
|
+
return this.request('StartFaceDetection', params as unknown as Record<string, unknown>)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Get results of face detection job
|
|
708
|
+
*/
|
|
709
|
+
async getFaceDetection(params: GetFaceDetectionCommandInput): Promise<GetFaceDetectionCommandOutput> {
|
|
710
|
+
return this.request('GetFaceDetection', params as unknown as Record<string, unknown>)
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Start async content moderation on video
|
|
715
|
+
*/
|
|
716
|
+
async startContentModeration(params: StartContentModerationCommandInput): Promise<StartContentModerationCommandOutput> {
|
|
717
|
+
return this.request('StartContentModeration', params as unknown as Record<string, unknown>)
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Get results of content moderation job
|
|
722
|
+
*/
|
|
723
|
+
async getContentModeration(params: GetContentModerationCommandInput): Promise<GetContentModerationCommandOutput> {
|
|
724
|
+
return this.request('GetContentModeration', params as unknown as Record<string, unknown>)
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// -------------------------------------------------------------------------
|
|
728
|
+
// Convenience Methods
|
|
729
|
+
// -------------------------------------------------------------------------
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Analyze an image from S3 and get all labels
|
|
733
|
+
*/
|
|
734
|
+
async analyzeS3Image(bucket: string, key: string, options?: { maxLabels?: number; minConfidence?: number }): Promise<Label[]> {
|
|
735
|
+
const result = await this.detectLabels({
|
|
736
|
+
Image: { S3Object: { Bucket: bucket, Name: key } },
|
|
737
|
+
MaxLabels: options?.maxLabels,
|
|
738
|
+
MinConfidence: options?.minConfidence,
|
|
739
|
+
})
|
|
740
|
+
return result.Labels || []
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Analyze image bytes and get all labels
|
|
745
|
+
*/
|
|
746
|
+
async analyzeImageBytes(bytes: Uint8Array, options?: { maxLabels?: number; minConfidence?: number }): Promise<Label[]> {
|
|
747
|
+
const result = await this.detectLabels({
|
|
748
|
+
Image: { Bytes: bytes },
|
|
749
|
+
MaxLabels: options?.maxLabels,
|
|
750
|
+
MinConfidence: options?.minConfidence,
|
|
751
|
+
})
|
|
752
|
+
return result.Labels || []
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Check if image is safe (no moderation labels above threshold)
|
|
757
|
+
*/
|
|
758
|
+
async isImageSafe(image: Image, threshold: number = 50): Promise<boolean> {
|
|
759
|
+
const result = await this.detectModerationLabels({
|
|
760
|
+
Image: image,
|
|
761
|
+
MinConfidence: threshold,
|
|
762
|
+
})
|
|
763
|
+
return (result.ModerationLabels?.length || 0) === 0
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Count faces in an image
|
|
768
|
+
*/
|
|
769
|
+
async countFaces(image: Image): Promise<number> {
|
|
770
|
+
const result = await this.detectFaces({
|
|
771
|
+
Image: image,
|
|
772
|
+
Attributes: ['DEFAULT'],
|
|
773
|
+
})
|
|
774
|
+
return result.FaceDetails?.length || 0
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Extract text from an image
|
|
779
|
+
*/
|
|
780
|
+
async extractText(image: Image): Promise<string[]> {
|
|
781
|
+
const result = await this.detectText({ Image: image })
|
|
782
|
+
return result.TextDetections?.filter(t => t.Type === 'LINE').map(t => t.DetectedText || '') || []
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Find matching face in collection
|
|
787
|
+
*/
|
|
788
|
+
async findFace(collectionId: string, image: Image, threshold: number = 80): Promise<FaceMatch | null> {
|
|
789
|
+
try {
|
|
790
|
+
const result = await this.searchFacesByImage({
|
|
791
|
+
CollectionId: collectionId,
|
|
792
|
+
Image: image,
|
|
793
|
+
FaceMatchThreshold: threshold,
|
|
794
|
+
MaxFaces: 1,
|
|
795
|
+
})
|
|
796
|
+
return result.FaceMatches?.[0] || null
|
|
797
|
+
} catch (error: unknown) {
|
|
798
|
+
// Face not found
|
|
799
|
+
if (error instanceof Error && error.message.includes('InvalidParameterException')) {
|
|
800
|
+
return null
|
|
801
|
+
}
|
|
802
|
+
throw error
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// ============================================================================
|
|
808
|
+
// Helper Functions
|
|
809
|
+
// ============================================================================
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Quick image label detection from S3
|
|
813
|
+
*/
|
|
814
|
+
export async function detectLabelsFromS3(
|
|
815
|
+
bucket: string,
|
|
816
|
+
key: string,
|
|
817
|
+
options?: { region?: string; maxLabels?: number },
|
|
818
|
+
): Promise<string[]> {
|
|
819
|
+
const client = new RekognitionClient(options?.region || 'us-east-1')
|
|
820
|
+
const labels = await client.analyzeS3Image(bucket, key, { maxLabels: options?.maxLabels })
|
|
821
|
+
return labels.map(l => l.Name || '').filter(Boolean)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Quick face count from S3 image
|
|
826
|
+
*/
|
|
827
|
+
export async function countFacesInS3Image(
|
|
828
|
+
bucket: string,
|
|
829
|
+
key: string,
|
|
830
|
+
region?: string,
|
|
831
|
+
): Promise<number> {
|
|
832
|
+
const client = new RekognitionClient(region || 'us-east-1')
|
|
833
|
+
return client.countFaces({ S3Object: { Bucket: bucket, Name: key } })
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Quick content moderation check
|
|
838
|
+
*/
|
|
839
|
+
export async function isContentSafe(
|
|
840
|
+
bucket: string,
|
|
841
|
+
key: string,
|
|
842
|
+
options?: { region?: string; threshold?: number },
|
|
843
|
+
): Promise<boolean> {
|
|
844
|
+
const client = new RekognitionClient(options?.region || 'us-east-1')
|
|
845
|
+
return client.isImageSafe({ S3Object: { Bucket: bucket, Name: key } }, options?.threshold)
|
|
846
|
+
}
|