@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.
Files changed (75) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/package.json +18 -16
  3. package/src/aws/acm.ts +768 -0
  4. package/src/aws/application-autoscaling.ts +845 -0
  5. package/src/aws/bedrock.ts +4074 -0
  6. package/src/aws/client.ts +891 -0
  7. package/src/aws/cloudformation.ts +896 -0
  8. package/src/aws/cloudfront.ts +1531 -0
  9. package/src/aws/cloudwatch-logs.ts +154 -0
  10. package/src/aws/comprehend.ts +839 -0
  11. package/src/aws/connect.ts +1056 -0
  12. package/src/aws/deploy-imap.ts +384 -0
  13. package/src/aws/dynamodb.ts +340 -0
  14. package/src/aws/ec2.ts +1385 -0
  15. package/src/aws/ecr.ts +621 -0
  16. package/src/aws/ecs.ts +615 -0
  17. package/src/aws/elasticache.ts +301 -0
  18. package/src/aws/elbv2.ts +942 -0
  19. package/src/aws/email.ts +928 -0
  20. package/src/aws/eventbridge.ts +248 -0
  21. package/src/aws/iam.ts +1689 -0
  22. package/src/aws/imap-server.ts +2100 -0
  23. package/src/aws/index.ts +213 -0
  24. package/src/aws/kendra.ts +1097 -0
  25. package/src/aws/lambda.ts +786 -0
  26. package/src/aws/opensearch.ts +158 -0
  27. package/src/aws/personalize.ts +977 -0
  28. package/src/aws/polly.ts +559 -0
  29. package/src/aws/rds.ts +888 -0
  30. package/src/aws/rekognition.ts +846 -0
  31. package/src/aws/route53-domains.ts +359 -0
  32. package/src/aws/route53.ts +1046 -0
  33. package/src/aws/s3.ts +2334 -0
  34. package/src/aws/scheduler.ts +571 -0
  35. package/src/aws/secrets-manager.ts +769 -0
  36. package/src/aws/ses.ts +1081 -0
  37. package/src/aws/setup-phone.ts +104 -0
  38. package/src/aws/setup-sms.ts +580 -0
  39. package/src/aws/sms.ts +1735 -0
  40. package/src/aws/smtp-server.ts +531 -0
  41. package/src/aws/sns.ts +758 -0
  42. package/src/aws/sqs.ts +382 -0
  43. package/src/aws/ssm.ts +807 -0
  44. package/src/aws/sts.ts +92 -0
  45. package/src/aws/support.ts +391 -0
  46. package/src/aws/test-imap.ts +86 -0
  47. package/src/aws/textract.ts +780 -0
  48. package/src/aws/transcribe.ts +108 -0
  49. package/src/aws/translate.ts +641 -0
  50. package/src/aws/voice.ts +1379 -0
  51. package/src/config.ts +35 -0
  52. package/src/deploy/index.ts +7 -0
  53. package/src/deploy/static-site-external-dns.ts +945 -0
  54. package/src/deploy/static-site.ts +1175 -0
  55. package/src/dns/cloudflare.ts +548 -0
  56. package/src/dns/godaddy.ts +412 -0
  57. package/src/dns/index.ts +205 -0
  58. package/src/dns/porkbun.ts +362 -0
  59. package/src/dns/route53-adapter.ts +414 -0
  60. package/src/dns/types.ts +119 -0
  61. package/src/dns/validator.ts +369 -0
  62. package/src/generators/index.ts +5 -0
  63. package/src/generators/infrastructure.ts +1660 -0
  64. package/src/index.ts +163 -0
  65. package/src/push/apns.ts +452 -0
  66. package/src/push/fcm.ts +506 -0
  67. package/src/push/index.ts +58 -0
  68. package/src/security/pre-deploy-scanner.ts +655 -0
  69. package/src/ssl/acme-client.ts +478 -0
  70. package/src/ssl/index.ts +7 -0
  71. package/src/ssl/letsencrypt.ts +747 -0
  72. package/src/types.ts +2 -0
  73. package/src/utils/cli.ts +398 -0
  74. package/src/validation/index.ts +5 -0
  75. 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
+ }