@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,780 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Textract Client
|
|
3
|
+
* Document OCR, form extraction, table extraction
|
|
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 Document {
|
|
20
|
+
Bytes?: Uint8Array
|
|
21
|
+
S3Object?: S3Object
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface BoundingBox {
|
|
25
|
+
Width?: number
|
|
26
|
+
Height?: number
|
|
27
|
+
Left?: number
|
|
28
|
+
Top?: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Point {
|
|
32
|
+
X?: number
|
|
33
|
+
Y?: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Geometry {
|
|
37
|
+
BoundingBox?: BoundingBox
|
|
38
|
+
Polygon?: Point[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Relationship {
|
|
42
|
+
Type?: 'VALUE' | 'CHILD' | 'COMPLEX_FEATURES' | 'MERGED_CELL' | 'TITLE' | 'ANSWER' | 'TABLE' | 'TABLE_TITLE' | 'TABLE_FOOTER'
|
|
43
|
+
Ids?: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Block {
|
|
47
|
+
BlockType?: 'KEY_VALUE_SET' | 'PAGE' | 'LINE' | 'WORD' | 'TABLE' | 'CELL' | 'SELECTION_ELEMENT' | 'MERGED_CELL' | 'TITLE' | 'QUERY' | 'QUERY_RESULT' | 'SIGNATURE' | 'TABLE_TITLE' | 'TABLE_FOOTER' | 'LAYOUT_TEXT' | 'LAYOUT_TITLE' | 'LAYOUT_HEADER' | 'LAYOUT_FOOTER' | 'LAYOUT_SECTION_HEADER' | 'LAYOUT_PAGE_NUMBER' | 'LAYOUT_LIST' | 'LAYOUT_FIGURE' | 'LAYOUT_TABLE' | 'LAYOUT_KEY_VALUE'
|
|
48
|
+
Confidence?: number
|
|
49
|
+
Text?: string
|
|
50
|
+
TextType?: 'HANDWRITING' | 'PRINTED'
|
|
51
|
+
RowIndex?: number
|
|
52
|
+
ColumnIndex?: number
|
|
53
|
+
RowSpan?: number
|
|
54
|
+
ColumnSpan?: number
|
|
55
|
+
Geometry?: Geometry
|
|
56
|
+
Id?: string
|
|
57
|
+
Relationships?: Relationship[]
|
|
58
|
+
EntityTypes?: ('KEY' | 'VALUE' | 'COLUMN_HEADER' | 'TABLE_TITLE' | 'TABLE_FOOTER' | 'TABLE_SECTION_TITLE' | 'TABLE_SUMMARY' | 'STRUCTURED_TABLE' | 'SEMI_STRUCTURED_TABLE')[]
|
|
59
|
+
SelectionStatus?: 'SELECTED' | 'NOT_SELECTED'
|
|
60
|
+
Page?: number
|
|
61
|
+
Query?: {
|
|
62
|
+
Text: string
|
|
63
|
+
Alias?: string
|
|
64
|
+
Pages?: string[]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DocumentMetadata {
|
|
69
|
+
Pages?: number
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Warning {
|
|
73
|
+
ErrorCode?: string
|
|
74
|
+
Pages?: number[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface DetectDocumentTextCommandInput {
|
|
78
|
+
Document: Document
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DetectDocumentTextCommandOutput {
|
|
82
|
+
DocumentMetadata?: DocumentMetadata
|
|
83
|
+
Blocks?: Block[]
|
|
84
|
+
DetectDocumentTextModelVersion?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface AnalyzeDocumentCommandInput {
|
|
88
|
+
Document: Document
|
|
89
|
+
FeatureTypes: ('TABLES' | 'FORMS' | 'QUERIES' | 'SIGNATURES' | 'LAYOUT')[]
|
|
90
|
+
HumanLoopConfig?: {
|
|
91
|
+
HumanLoopName: string
|
|
92
|
+
FlowDefinitionArn: string
|
|
93
|
+
DataAttributes?: {
|
|
94
|
+
ContentClassifiers?: ('FreeOfPersonallyIdentifiableInformation' | 'FreeOfAdultContent')[]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
QueriesConfig?: {
|
|
98
|
+
Queries: Array<{
|
|
99
|
+
Text: string
|
|
100
|
+
Alias?: string
|
|
101
|
+
Pages?: string[]
|
|
102
|
+
}>
|
|
103
|
+
}
|
|
104
|
+
AdaptersConfig?: {
|
|
105
|
+
Adapters: Array<{
|
|
106
|
+
AdapterId: string
|
|
107
|
+
Pages?: string[]
|
|
108
|
+
Version: string
|
|
109
|
+
}>
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface AnalyzeDocumentCommandOutput {
|
|
114
|
+
DocumentMetadata?: DocumentMetadata
|
|
115
|
+
Blocks?: Block[]
|
|
116
|
+
HumanLoopActivationOutput?: {
|
|
117
|
+
HumanLoopArn?: string
|
|
118
|
+
HumanLoopActivationReasons?: string[]
|
|
119
|
+
HumanLoopActivationConditionsEvaluationResults?: string
|
|
120
|
+
}
|
|
121
|
+
AnalyzeDocumentModelVersion?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface AnalyzeExpenseCommandInput {
|
|
125
|
+
Document: Document
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ExpenseField {
|
|
129
|
+
Type?: {
|
|
130
|
+
Text?: string
|
|
131
|
+
Confidence?: number
|
|
132
|
+
}
|
|
133
|
+
LabelDetection?: {
|
|
134
|
+
Text?: string
|
|
135
|
+
Geometry?: Geometry
|
|
136
|
+
Confidence?: number
|
|
137
|
+
}
|
|
138
|
+
ValueDetection?: {
|
|
139
|
+
Text?: string
|
|
140
|
+
Geometry?: Geometry
|
|
141
|
+
Confidence?: number
|
|
142
|
+
}
|
|
143
|
+
PageNumber?: number
|
|
144
|
+
Currency?: {
|
|
145
|
+
Code?: string
|
|
146
|
+
Confidence?: number
|
|
147
|
+
}
|
|
148
|
+
GroupProperties?: Array<{
|
|
149
|
+
Types?: string[]
|
|
150
|
+
Id?: string
|
|
151
|
+
}>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface LineItemGroup {
|
|
155
|
+
LineItemGroupIndex?: number
|
|
156
|
+
LineItems?: Array<{
|
|
157
|
+
LineItemExpenseFields?: ExpenseField[]
|
|
158
|
+
}>
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface ExpenseDocument {
|
|
162
|
+
ExpenseIndex?: number
|
|
163
|
+
SummaryFields?: ExpenseField[]
|
|
164
|
+
LineItemGroups?: LineItemGroup[]
|
|
165
|
+
Blocks?: Block[]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface AnalyzeExpenseCommandOutput {
|
|
169
|
+
DocumentMetadata?: DocumentMetadata
|
|
170
|
+
ExpenseDocuments?: ExpenseDocument[]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface AnalyzeIDCommandInput {
|
|
174
|
+
DocumentPages: Document[]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface IdentityDocument {
|
|
178
|
+
DocumentIndex?: number
|
|
179
|
+
IdentityDocumentFields?: Array<{
|
|
180
|
+
Type?: {
|
|
181
|
+
Text?: string
|
|
182
|
+
Confidence?: number
|
|
183
|
+
}
|
|
184
|
+
ValueDetection?: {
|
|
185
|
+
Text?: string
|
|
186
|
+
NormalizedValue?: {
|
|
187
|
+
Value?: string
|
|
188
|
+
ValueType?: 'DATE'
|
|
189
|
+
}
|
|
190
|
+
Confidence?: number
|
|
191
|
+
}
|
|
192
|
+
}>
|
|
193
|
+
Blocks?: Block[]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface AnalyzeIDCommandOutput {
|
|
197
|
+
IdentityDocuments?: IdentityDocument[]
|
|
198
|
+
DocumentMetadata?: DocumentMetadata
|
|
199
|
+
AnalyzeIDModelVersion?: string
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface StartDocumentTextDetectionCommandInput {
|
|
203
|
+
DocumentLocation: {
|
|
204
|
+
S3Object?: S3Object
|
|
205
|
+
}
|
|
206
|
+
ClientRequestToken?: string
|
|
207
|
+
JobTag?: string
|
|
208
|
+
NotificationChannel?: {
|
|
209
|
+
SNSTopicArn: string
|
|
210
|
+
RoleArn: string
|
|
211
|
+
}
|
|
212
|
+
OutputConfig?: {
|
|
213
|
+
S3Bucket: string
|
|
214
|
+
S3Prefix?: string
|
|
215
|
+
}
|
|
216
|
+
KMSKeyId?: string
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface StartDocumentTextDetectionCommandOutput {
|
|
220
|
+
JobId?: string
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface GetDocumentTextDetectionCommandInput {
|
|
224
|
+
JobId: string
|
|
225
|
+
MaxResults?: number
|
|
226
|
+
NextToken?: string
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface GetDocumentTextDetectionCommandOutput {
|
|
230
|
+
DocumentMetadata?: DocumentMetadata
|
|
231
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED' | 'PARTIAL_SUCCESS'
|
|
232
|
+
NextToken?: string
|
|
233
|
+
Blocks?: Block[]
|
|
234
|
+
Warnings?: Warning[]
|
|
235
|
+
StatusMessage?: string
|
|
236
|
+
DetectDocumentTextModelVersion?: string
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface StartDocumentAnalysisCommandInput {
|
|
240
|
+
DocumentLocation: {
|
|
241
|
+
S3Object?: S3Object
|
|
242
|
+
}
|
|
243
|
+
FeatureTypes: ('TABLES' | 'FORMS' | 'QUERIES' | 'SIGNATURES' | 'LAYOUT')[]
|
|
244
|
+
ClientRequestToken?: string
|
|
245
|
+
JobTag?: string
|
|
246
|
+
NotificationChannel?: {
|
|
247
|
+
SNSTopicArn: string
|
|
248
|
+
RoleArn: string
|
|
249
|
+
}
|
|
250
|
+
OutputConfig?: {
|
|
251
|
+
S3Bucket: string
|
|
252
|
+
S3Prefix?: string
|
|
253
|
+
}
|
|
254
|
+
KMSKeyId?: string
|
|
255
|
+
QueriesConfig?: {
|
|
256
|
+
Queries: Array<{
|
|
257
|
+
Text: string
|
|
258
|
+
Alias?: string
|
|
259
|
+
Pages?: string[]
|
|
260
|
+
}>
|
|
261
|
+
}
|
|
262
|
+
AdaptersConfig?: {
|
|
263
|
+
Adapters: Array<{
|
|
264
|
+
AdapterId: string
|
|
265
|
+
Pages?: string[]
|
|
266
|
+
Version: string
|
|
267
|
+
}>
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface StartDocumentAnalysisCommandOutput {
|
|
272
|
+
JobId?: string
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface GetDocumentAnalysisCommandInput {
|
|
276
|
+
JobId: string
|
|
277
|
+
MaxResults?: number
|
|
278
|
+
NextToken?: string
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface GetDocumentAnalysisCommandOutput {
|
|
282
|
+
DocumentMetadata?: DocumentMetadata
|
|
283
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED' | 'PARTIAL_SUCCESS'
|
|
284
|
+
NextToken?: string
|
|
285
|
+
Blocks?: Block[]
|
|
286
|
+
Warnings?: Warning[]
|
|
287
|
+
StatusMessage?: string
|
|
288
|
+
AnalyzeDocumentModelVersion?: string
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface StartExpenseAnalysisCommandInput {
|
|
292
|
+
DocumentLocation: {
|
|
293
|
+
S3Object?: S3Object
|
|
294
|
+
}
|
|
295
|
+
ClientRequestToken?: string
|
|
296
|
+
JobTag?: string
|
|
297
|
+
NotificationChannel?: {
|
|
298
|
+
SNSTopicArn: string
|
|
299
|
+
RoleArn: string
|
|
300
|
+
}
|
|
301
|
+
OutputConfig?: {
|
|
302
|
+
S3Bucket: string
|
|
303
|
+
S3Prefix?: string
|
|
304
|
+
}
|
|
305
|
+
KMSKeyId?: string
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface StartExpenseAnalysisCommandOutput {
|
|
309
|
+
JobId?: string
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface GetExpenseAnalysisCommandInput {
|
|
313
|
+
JobId: string
|
|
314
|
+
MaxResults?: number
|
|
315
|
+
NextToken?: string
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface GetExpenseAnalysisCommandOutput {
|
|
319
|
+
DocumentMetadata?: DocumentMetadata
|
|
320
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED' | 'PARTIAL_SUCCESS'
|
|
321
|
+
NextToken?: string
|
|
322
|
+
ExpenseDocuments?: ExpenseDocument[]
|
|
323
|
+
Warnings?: Warning[]
|
|
324
|
+
StatusMessage?: string
|
|
325
|
+
AnalyzeExpenseModelVersion?: string
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface StartLendingAnalysisCommandInput {
|
|
329
|
+
DocumentLocation: {
|
|
330
|
+
S3Object?: S3Object
|
|
331
|
+
}
|
|
332
|
+
ClientRequestToken?: string
|
|
333
|
+
JobTag?: string
|
|
334
|
+
NotificationChannel?: {
|
|
335
|
+
SNSTopicArn: string
|
|
336
|
+
RoleArn: string
|
|
337
|
+
}
|
|
338
|
+
OutputConfig?: {
|
|
339
|
+
S3Bucket: string
|
|
340
|
+
S3Prefix?: string
|
|
341
|
+
}
|
|
342
|
+
KMSKeyId?: string
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export interface StartLendingAnalysisCommandOutput {
|
|
346
|
+
JobId?: string
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export interface GetLendingAnalysisCommandInput {
|
|
350
|
+
JobId: string
|
|
351
|
+
MaxResults?: number
|
|
352
|
+
NextToken?: string
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export interface LendingDocument {
|
|
356
|
+
LendingFields?: Array<{
|
|
357
|
+
Type?: string
|
|
358
|
+
KeyDetection?: {
|
|
359
|
+
Text?: string
|
|
360
|
+
Geometry?: Geometry
|
|
361
|
+
Confidence?: number
|
|
362
|
+
}
|
|
363
|
+
ValueDetections?: Array<{
|
|
364
|
+
Text?: string
|
|
365
|
+
Geometry?: Geometry
|
|
366
|
+
Confidence?: number
|
|
367
|
+
SelectionStatus?: 'SELECTED' | 'NOT_SELECTED'
|
|
368
|
+
}>
|
|
369
|
+
}>
|
|
370
|
+
SignatureDetections?: Array<{
|
|
371
|
+
Confidence?: number
|
|
372
|
+
Geometry?: Geometry
|
|
373
|
+
}>
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface LendingResult {
|
|
377
|
+
Page?: number
|
|
378
|
+
PageClassification?: {
|
|
379
|
+
PageType?: Array<{
|
|
380
|
+
Value?: string
|
|
381
|
+
Confidence?: number
|
|
382
|
+
}>
|
|
383
|
+
PageNumber?: Array<{
|
|
384
|
+
Value?: string
|
|
385
|
+
Confidence?: number
|
|
386
|
+
}>
|
|
387
|
+
}
|
|
388
|
+
Extractions?: Array<{
|
|
389
|
+
LendingDocument?: LendingDocument
|
|
390
|
+
ExpenseDocument?: ExpenseDocument
|
|
391
|
+
IdentityDocument?: IdentityDocument
|
|
392
|
+
}>
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface GetLendingAnalysisCommandOutput {
|
|
396
|
+
DocumentMetadata?: DocumentMetadata
|
|
397
|
+
JobStatus?: 'IN_PROGRESS' | 'SUCCEEDED' | 'FAILED' | 'PARTIAL_SUCCESS'
|
|
398
|
+
NextToken?: string
|
|
399
|
+
Results?: LendingResult[]
|
|
400
|
+
Warnings?: Warning[]
|
|
401
|
+
StatusMessage?: string
|
|
402
|
+
AnalyzeLendingModelVersion?: string
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// Textract Client
|
|
407
|
+
// ============================================================================
|
|
408
|
+
|
|
409
|
+
export class TextractClient {
|
|
410
|
+
private client: AWSClient
|
|
411
|
+
private region: string
|
|
412
|
+
|
|
413
|
+
constructor(region: string = 'us-east-1') {
|
|
414
|
+
this.region = region
|
|
415
|
+
this.client = new AWSClient()
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private async request<T>(action: string, params: Record<string, unknown>): Promise<T> {
|
|
419
|
+
return this.client.request({
|
|
420
|
+
service: 'textract',
|
|
421
|
+
region: this.region,
|
|
422
|
+
method: 'POST',
|
|
423
|
+
path: '/',
|
|
424
|
+
headers: {
|
|
425
|
+
'Content-Type': 'application/x-amz-json-1.1',
|
|
426
|
+
'X-Amz-Target': `Textract.${action}`,
|
|
427
|
+
},
|
|
428
|
+
body: JSON.stringify(params),
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// -------------------------------------------------------------------------
|
|
433
|
+
// Synchronous Operations
|
|
434
|
+
// -------------------------------------------------------------------------
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Detect text in a document (OCR)
|
|
438
|
+
*/
|
|
439
|
+
async detectDocumentText(params: DetectDocumentTextCommandInput): Promise<DetectDocumentTextCommandOutput> {
|
|
440
|
+
return this.request('DetectDocumentText', params as unknown as Record<string, unknown>)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Analyze a document (forms, tables, queries)
|
|
445
|
+
*/
|
|
446
|
+
async analyzeDocument(params: AnalyzeDocumentCommandInput): Promise<AnalyzeDocumentCommandOutput> {
|
|
447
|
+
return this.request('AnalyzeDocument', params as unknown as Record<string, unknown>)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Analyze expense document (receipts, invoices)
|
|
452
|
+
*/
|
|
453
|
+
async analyzeExpense(params: AnalyzeExpenseCommandInput): Promise<AnalyzeExpenseCommandOutput> {
|
|
454
|
+
return this.request('AnalyzeExpense', params as unknown as Record<string, unknown>)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Analyze ID document (driver's license, passport)
|
|
459
|
+
*/
|
|
460
|
+
async analyzeID(params: AnalyzeIDCommandInput): Promise<AnalyzeIDCommandOutput> {
|
|
461
|
+
return this.request('AnalyzeID', params as unknown as Record<string, unknown>)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// -------------------------------------------------------------------------
|
|
465
|
+
// Asynchronous Operations
|
|
466
|
+
// -------------------------------------------------------------------------
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Start async text detection job
|
|
470
|
+
*/
|
|
471
|
+
async startDocumentTextDetection(params: StartDocumentTextDetectionCommandInput): Promise<StartDocumentTextDetectionCommandOutput> {
|
|
472
|
+
return this.request('StartDocumentTextDetection', params as unknown as Record<string, unknown>)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Get results of text detection job
|
|
477
|
+
*/
|
|
478
|
+
async getDocumentTextDetection(params: GetDocumentTextDetectionCommandInput): Promise<GetDocumentTextDetectionCommandOutput> {
|
|
479
|
+
return this.request('GetDocumentTextDetection', params as unknown as Record<string, unknown>)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Start async document analysis job
|
|
484
|
+
*/
|
|
485
|
+
async startDocumentAnalysis(params: StartDocumentAnalysisCommandInput): Promise<StartDocumentAnalysisCommandOutput> {
|
|
486
|
+
return this.request('StartDocumentAnalysis', params as unknown as Record<string, unknown>)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get results of document analysis job
|
|
491
|
+
*/
|
|
492
|
+
async getDocumentAnalysis(params: GetDocumentAnalysisCommandInput): Promise<GetDocumentAnalysisCommandOutput> {
|
|
493
|
+
return this.request('GetDocumentAnalysis', params as unknown as Record<string, unknown>)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Start async expense analysis job
|
|
498
|
+
*/
|
|
499
|
+
async startExpenseAnalysis(params: StartExpenseAnalysisCommandInput): Promise<StartExpenseAnalysisCommandOutput> {
|
|
500
|
+
return this.request('StartExpenseAnalysis', params as unknown as Record<string, unknown>)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Get results of expense analysis job
|
|
505
|
+
*/
|
|
506
|
+
async getExpenseAnalysis(params: GetExpenseAnalysisCommandInput): Promise<GetExpenseAnalysisCommandOutput> {
|
|
507
|
+
return this.request('GetExpenseAnalysis', params as unknown as Record<string, unknown>)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Start async lending analysis job
|
|
512
|
+
*/
|
|
513
|
+
async startLendingAnalysis(params: StartLendingAnalysisCommandInput): Promise<StartLendingAnalysisCommandOutput> {
|
|
514
|
+
return this.request('StartLendingAnalysis', params as unknown as Record<string, unknown>)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Get results of lending analysis job
|
|
519
|
+
*/
|
|
520
|
+
async getLendingAnalysis(params: GetLendingAnalysisCommandInput): Promise<GetLendingAnalysisCommandOutput> {
|
|
521
|
+
return this.request('GetLendingAnalysis', params as unknown as Record<string, unknown>)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// -------------------------------------------------------------------------
|
|
525
|
+
// Convenience Methods
|
|
526
|
+
// -------------------------------------------------------------------------
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Extract all text from a document
|
|
530
|
+
*/
|
|
531
|
+
async extractText(document: Document): Promise<string[]> {
|
|
532
|
+
const result = await this.detectDocumentText({ Document: document })
|
|
533
|
+
return result.Blocks?.filter(b => b.BlockType === 'LINE').map(b => b.Text || '') || []
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Extract text from S3 document
|
|
538
|
+
*/
|
|
539
|
+
async extractTextFromS3(bucket: string, key: string): Promise<string[]> {
|
|
540
|
+
return this.extractText({ S3Object: { Bucket: bucket, Name: key } })
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Extract key-value pairs (forms) from a document
|
|
545
|
+
*/
|
|
546
|
+
async extractForms(document: Document): Promise<Array<{ key: string; value: string; confidence: number }>> {
|
|
547
|
+
const result = await this.analyzeDocument({
|
|
548
|
+
Document: document,
|
|
549
|
+
FeatureTypes: ['FORMS'],
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
const blocks = result.Blocks || []
|
|
553
|
+
const blockMap = new Map<string, Block>()
|
|
554
|
+
blocks.forEach(b => b.Id && blockMap.set(b.Id, b))
|
|
555
|
+
|
|
556
|
+
const forms: Array<{ key: string; value: string; confidence: number }> = []
|
|
557
|
+
|
|
558
|
+
for (const block of blocks) {
|
|
559
|
+
if (block.BlockType === 'KEY_VALUE_SET' && block.EntityTypes?.includes('KEY')) {
|
|
560
|
+
const keyText = this.getBlockText(block, blockMap)
|
|
561
|
+
const valueBlock = block.Relationships?.find(r => r.Type === 'VALUE')
|
|
562
|
+
let valueText = ''
|
|
563
|
+
if (valueBlock?.Ids) {
|
|
564
|
+
for (const id of valueBlock.Ids) {
|
|
565
|
+
const vb = blockMap.get(id)
|
|
566
|
+
if (vb) valueText += this.getBlockText(vb, blockMap) + ' '
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
forms.push({
|
|
570
|
+
key: keyText.trim(),
|
|
571
|
+
value: valueText.trim(),
|
|
572
|
+
confidence: block.Confidence || 0,
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return forms
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Extract tables from a document
|
|
582
|
+
*/
|
|
583
|
+
async extractTables(document: Document): Promise<Array<{ rows: string[][] }>> {
|
|
584
|
+
const result = await this.analyzeDocument({
|
|
585
|
+
Document: document,
|
|
586
|
+
FeatureTypes: ['TABLES'],
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
const blocks = result.Blocks || []
|
|
590
|
+
const blockMap = new Map<string, Block>()
|
|
591
|
+
blocks.forEach(b => b.Id && blockMap.set(b.Id, b))
|
|
592
|
+
|
|
593
|
+
const tables: Array<{ rows: string[][] }> = []
|
|
594
|
+
|
|
595
|
+
for (const block of blocks) {
|
|
596
|
+
if (block.BlockType === 'TABLE') {
|
|
597
|
+
const cellIds = block.Relationships?.find(r => r.Type === 'CHILD')?.Ids || []
|
|
598
|
+
const cells: Block[] = cellIds.map(id => blockMap.get(id)).filter(Boolean) as Block[]
|
|
599
|
+
|
|
600
|
+
// Find max row and column
|
|
601
|
+
let maxRow = 0
|
|
602
|
+
let maxCol = 0
|
|
603
|
+
for (const cell of cells) {
|
|
604
|
+
if (cell.RowIndex && cell.RowIndex > maxRow) maxRow = cell.RowIndex
|
|
605
|
+
if (cell.ColumnIndex && cell.ColumnIndex > maxCol) maxCol = cell.ColumnIndex
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Build 2D array
|
|
609
|
+
const rows: string[][] = Array.from({ length: maxRow }, () => Array.from({ length: maxCol }, () => ''))
|
|
610
|
+
|
|
611
|
+
for (const cell of cells) {
|
|
612
|
+
if (cell.RowIndex && cell.ColumnIndex) {
|
|
613
|
+
rows[cell.RowIndex - 1][cell.ColumnIndex - 1] = this.getBlockText(cell, blockMap)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
tables.push({ rows })
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return tables
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Extract expense summary from receipt/invoice
|
|
626
|
+
*/
|
|
627
|
+
async extractExpenseSummary(document: Document): Promise<{
|
|
628
|
+
vendor?: string
|
|
629
|
+
total?: string
|
|
630
|
+
date?: string
|
|
631
|
+
items: Array<{ description?: string; quantity?: string; price?: string }>
|
|
632
|
+
}> {
|
|
633
|
+
const result = await this.analyzeExpense({ Document: document })
|
|
634
|
+
const expense = result.ExpenseDocuments?.[0]
|
|
635
|
+
|
|
636
|
+
if (!expense) {
|
|
637
|
+
return { items: [] }
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const summary: { vendor?: string; total?: string; date?: string } = {}
|
|
641
|
+
|
|
642
|
+
for (const field of expense.SummaryFields || []) {
|
|
643
|
+
const type = field.Type?.Text?.toUpperCase()
|
|
644
|
+
const value = field.ValueDetection?.Text
|
|
645
|
+
|
|
646
|
+
if (type === 'VENDOR_NAME') summary.vendor = value
|
|
647
|
+
if (type === 'TOTAL') summary.total = value
|
|
648
|
+
if (type === 'INVOICE_RECEIPT_DATE') summary.date = value
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const items: Array<{ description?: string; quantity?: string; price?: string }> = []
|
|
652
|
+
|
|
653
|
+
for (const group of expense.LineItemGroups || []) {
|
|
654
|
+
for (const lineItem of group.LineItems || []) {
|
|
655
|
+
const item: { description?: string; quantity?: string; price?: string } = {}
|
|
656
|
+
for (const field of lineItem.LineItemExpenseFields || []) {
|
|
657
|
+
const type = field.Type?.Text?.toUpperCase()
|
|
658
|
+
const value = field.ValueDetection?.Text
|
|
659
|
+
|
|
660
|
+
if (type === 'ITEM') item.description = value
|
|
661
|
+
if (type === 'QUANTITY') item.quantity = value
|
|
662
|
+
if (type === 'PRICE') item.price = value
|
|
663
|
+
}
|
|
664
|
+
if (item.description || item.price) items.push(item)
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return { ...summary, items }
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Answer questions about a document
|
|
673
|
+
*/
|
|
674
|
+
async queryDocument(document: Document, questions: string[]): Promise<Array<{ question: string; answer: string; confidence: number }>> {
|
|
675
|
+
const result = await this.analyzeDocument({
|
|
676
|
+
Document: document,
|
|
677
|
+
FeatureTypes: ['QUERIES'],
|
|
678
|
+
QueriesConfig: {
|
|
679
|
+
Queries: questions.map(q => ({ Text: q })),
|
|
680
|
+
},
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
const blocks = result.Blocks || []
|
|
684
|
+
const answers: Array<{ question: string; answer: string; confidence: number }> = []
|
|
685
|
+
|
|
686
|
+
for (const block of blocks) {
|
|
687
|
+
if (block.BlockType === 'QUERY_RESULT' && block.Text) {
|
|
688
|
+
// Find the corresponding query
|
|
689
|
+
const queryBlock = blocks.find(b =>
|
|
690
|
+
b.BlockType === 'QUERY' && b.Relationships?.some(r =>
|
|
691
|
+
r.Type === 'ANSWER' && r.Ids?.includes(block.Id || ''),
|
|
692
|
+
),
|
|
693
|
+
)
|
|
694
|
+
answers.push({
|
|
695
|
+
question: queryBlock?.Query?.Text || '',
|
|
696
|
+
answer: block.Text,
|
|
697
|
+
confidence: block.Confidence || 0,
|
|
698
|
+
})
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return answers
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Wait for async job to complete
|
|
707
|
+
*/
|
|
708
|
+
async waitForJob(
|
|
709
|
+
jobId: string,
|
|
710
|
+
getJob: (jobId: string) => Promise<{ JobStatus?: string }>,
|
|
711
|
+
options?: { maxWaitMs?: number; pollIntervalMs?: number },
|
|
712
|
+
): Promise<void> {
|
|
713
|
+
const maxWaitMs = options?.maxWaitMs ?? 300000 // 5 minutes
|
|
714
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 5000
|
|
715
|
+
const startTime = Date.now()
|
|
716
|
+
|
|
717
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
718
|
+
const result = await getJob(jobId)
|
|
719
|
+
if (result.JobStatus === 'SUCCEEDED' || result.JobStatus === 'PARTIAL_SUCCESS') {
|
|
720
|
+
return
|
|
721
|
+
}
|
|
722
|
+
if (result.JobStatus === 'FAILED') {
|
|
723
|
+
throw new Error(`Textract job ${jobId} failed`)
|
|
724
|
+
}
|
|
725
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs))
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
throw new Error(`Timeout waiting for Textract job ${jobId}`)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
private getBlockText(block: Block, blockMap: Map<string, Block>): string {
|
|
732
|
+
if (block.Text) return block.Text
|
|
733
|
+
|
|
734
|
+
const childIds = block.Relationships?.find(r => r.Type === 'CHILD')?.Ids || []
|
|
735
|
+
return childIds.map(id => blockMap.get(id)?.Text || '').join(' ')
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// ============================================================================
|
|
740
|
+
// Helper Functions
|
|
741
|
+
// ============================================================================
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Quick text extraction from S3 document
|
|
745
|
+
*/
|
|
746
|
+
export async function extractTextFromS3(
|
|
747
|
+
bucket: string,
|
|
748
|
+
key: string,
|
|
749
|
+
region?: string,
|
|
750
|
+
): Promise<string> {
|
|
751
|
+
const client = new TextractClient(region || 'us-east-1')
|
|
752
|
+
const lines = await client.extractTextFromS3(bucket, key)
|
|
753
|
+
return lines.join('\n')
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Quick form extraction from S3 document
|
|
758
|
+
*/
|
|
759
|
+
export async function extractFormsFromS3(
|
|
760
|
+
bucket: string,
|
|
761
|
+
key: string,
|
|
762
|
+
region?: string,
|
|
763
|
+
): Promise<Record<string, string>> {
|
|
764
|
+
const client = new TextractClient(region || 'us-east-1')
|
|
765
|
+
const forms = await client.extractForms({ S3Object: { Bucket: bucket, Name: key } })
|
|
766
|
+
return Object.fromEntries(forms.map(f => [f.key, f.value]))
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Quick table extraction from S3 document
|
|
771
|
+
*/
|
|
772
|
+
export async function extractTablesFromS3(
|
|
773
|
+
bucket: string,
|
|
774
|
+
key: string,
|
|
775
|
+
region?: string,
|
|
776
|
+
): Promise<string[][][]> {
|
|
777
|
+
const client = new TextractClient(region || 'us-east-1')
|
|
778
|
+
const tables = await client.extractTables({ S3Object: { Bucket: bucket, Name: key } })
|
|
779
|
+
return tables.map(t => t.rows)
|
|
780
|
+
}
|