document-ocr 0.0.0-stage → 3.1.3
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/LICENSE +21 -0
- package/README.md +260 -2
- package/dist/index.cjs +659 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +293 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.js +616 -0
- package/dist/index.js.map +1 -0
- package/hosting/.dockerignore +17 -0
- package/hosting/.gcloudignore +14 -0
- package/hosting/FEATURES.md +172 -0
- package/hosting/HOSTING.md +268 -0
- package/hosting/LICENSE +21 -0
- package/hosting/README.md +369 -0
- package/hosting/core/__init__.py +0 -0
- package/hosting/core/aadhaar_extractor.py +298 -0
- package/hosting/core/back_page_extractor.py +492 -0
- package/hosting/core/barcodes.py +125 -0
- package/hosting/core/card_recovery.py +244 -0
- package/hosting/core/document_bundle.py +82 -0
- package/hosting/core/document_classifier.py +233 -0
- package/hosting/core/document_input.py +135 -0
- package/hosting/core/document_registry.py +143 -0
- package/hosting/core/driving_licence_extractor.py +510 -0
- package/hosting/core/ead_recovery.py +54 -0
- package/hosting/core/evidence.py +54 -0
- package/hosting/core/form_recovery.py +61 -0
- package/hosting/core/jobs.py +232 -0
- package/hosting/core/kyc_ocr.py +132 -0
- package/hosting/core/kyc_validation.py +411 -0
- package/hosting/core/model_setup.py +13 -0
- package/hosting/core/mrz_parser.py +336 -0
- package/hosting/core/mrz_recovery.py +193 -0
- package/hosting/core/npr_extractor.py +410 -0
- package/hosting/core/nrega_extractor.py +827 -0
- package/hosting/core/ocr_engine.py +207 -0
- package/hosting/core/page_classifier.py +137 -0
- package/hosting/core/pan_extractor.py +183 -0
- package/hosting/core/pipeline.py +712 -0
- package/hosting/core/preprocessor.py +297 -0
- package/hosting/core/recovery_budget.py +67 -0
- package/hosting/core/redaction.py +48 -0
- package/hosting/core/review.html +244 -0
- package/hosting/core/structured_documents.py +422 -0
- package/hosting/core/travel_mrz.py +164 -0
- package/hosting/core/travel_recovery.py +187 -0
- package/hosting/core/validator.py +396 -0
- package/hosting/core/validators.py +179 -0
- package/hosting/core/voter_id_extractor.py +363 -0
- package/hosting/deploy/cloudflare/deploy.sh +44 -0
- package/hosting/deploy/cloudflare/package-lock.json +3088 -0
- package/hosting/deploy/cloudflare/package.json +29 -0
- package/hosting/deploy/cloudflare/src/gateway.ts +42 -0
- package/hosting/deploy/cloudflare/src/index.ts +21 -0
- package/hosting/deploy/cloudflare/test/gateway.test.ts +59 -0
- package/hosting/deploy/cloudflare/tsconfig.json +12 -0
- package/hosting/deploy/cloudflare/worker-configuration.d.ts +16253 -0
- package/hosting/deploy/cloudflare/wrangler.jsonc +26 -0
- package/hosting/deploy/cloudrun/cloudbuild.yaml +18 -0
- package/hosting/deploy/cloudrun/deploy.sh +71 -0
- package/hosting/deploy/cloudrun/render_service.py +47 -0
- package/hosting/deploy/common.sh +14 -0
- package/hosting/deploy/docker/Dockerfile +16 -0
- package/hosting/deploy/docker/server.py +313 -0
- package/hosting/deploy/lambda/Dockerfile.lambda +11 -0
- package/hosting/deploy/lambda/deploy.sh +22 -0
- package/hosting/deploy/lambda/handler.py +132 -0
- package/hosting/deploy/lambda/template.yaml +59 -0
- package/hosting/deploy/local/dev.sh +18 -0
- package/hosting/deploy/server/Caddyfile +5 -0
- package/hosting/deploy/server/compose.jobs.yaml +36 -0
- package/hosting/deploy/server/compose.yaml +38 -0
- package/hosting/deploy/server/deploy.sh +23 -0
- package/hosting/infra/cloudrun/main.tf +104 -0
- package/hosting/infra/cloudrun/terraform.tfvars.example +7 -0
- package/hosting/infra/cloudrun/variables.tf +55 -0
- package/hosting/infra/lambda/main.tf +63 -0
- package/hosting/infra/lambda/terraform.tfvars.example +6 -0
- package/hosting/infra/lambda/variables.tf +37 -0
- package/hosting/pyproject.toml +65 -0
- package/hosting/requirements.lock +134 -0
- package/package.json +76 -4
- package/python/core/__init__.py +0 -0
- package/python/core/aadhaar_extractor.py +298 -0
- package/python/core/back_page_extractor.py +492 -0
- package/python/core/barcodes.py +125 -0
- package/python/core/card_recovery.py +244 -0
- package/python/core/document_bundle.py +82 -0
- package/python/core/document_classifier.py +233 -0
- package/python/core/document_input.py +135 -0
- package/python/core/document_registry.py +143 -0
- package/python/core/driving_licence_extractor.py +510 -0
- package/python/core/ead_recovery.py +54 -0
- package/python/core/evidence.py +54 -0
- package/python/core/form_recovery.py +61 -0
- package/python/core/jobs.py +232 -0
- package/python/core/kyc_ocr.py +132 -0
- package/python/core/kyc_validation.py +411 -0
- package/python/core/model_setup.py +13 -0
- package/python/core/mrz_parser.py +336 -0
- package/python/core/mrz_recovery.py +193 -0
- package/python/core/npr_extractor.py +410 -0
- package/python/core/nrega_extractor.py +827 -0
- package/python/core/ocr_engine.py +207 -0
- package/python/core/page_classifier.py +137 -0
- package/python/core/pan_extractor.py +183 -0
- package/python/core/pipeline.py +712 -0
- package/python/core/preprocessor.py +297 -0
- package/python/core/recovery_budget.py +67 -0
- package/python/core/redaction.py +48 -0
- package/python/core/review.html +244 -0
- package/python/core/structured_documents.py +422 -0
- package/python/core/travel_mrz.py +164 -0
- package/python/core/travel_recovery.py +187 -0
- package/python/core/validator.py +396 -0
- package/python/core/validators.py +179 -0
- package/python/core/voter_id_extractor.py +363 -0
- package/python/pyproject.toml +34 -0
- package/python/requirements.lock +134 -0
- package/python/server.py +313 -0
- package/scripts/dococr.mjs +163 -0
- package/scripts/postinstall.mjs +173 -0
- package/scripts/sync-hosting.mjs +35 -0
- package/scripts/sync-python.sh +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Piyush Gambhir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,261 @@
|
|
|
1
|
-
#
|
|
1
|
+
# document-ocr
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Document OCR for Node.js 18+, with a bundled local Python/RapidOCR runtime and
|
|
4
|
+
HTTP/Lambda clients. It extracts structured fields from passports, Indian and
|
|
5
|
+
experimental US identity, immigration and tax documents. The server
|
|
6
|
+
catalog reports each family’s required fields and experimental status.
|
|
7
|
+
|
|
8
|
+
> This is beta extraction software. It does not verify document authenticity,
|
|
9
|
+
> detect fraud, query issuing authorities, or by itself satisfy KYC obligations.
|
|
10
|
+
> NREGA and NPR extraction is experimental until measured against a
|
|
11
|
+
> representative private image dataset.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install document-ocr
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
When npm blocks dependency install scripts, run `npm install-scripts approve
|
|
20
|
+
document-ocr` followed by `npm rebuild document-ocr` to enable local Python setup.
|
|
21
|
+
HTTP and Lambda client modes do not require that setup.
|
|
22
|
+
|
|
23
|
+
Development uses Node.js 26.12.1. Local OCR requires Python 3.14. If [`uv`](https://docs.astral.sh/uv/) is
|
|
24
|
+
available, the postinstall script can create the local environment
|
|
25
|
+
automatically.
|
|
26
|
+
|
|
27
|
+
## Local mode
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { DocumentOCR } from 'document-ocr'
|
|
31
|
+
|
|
32
|
+
const ocr = new DocumentOCR()
|
|
33
|
+
const result = await ocr.scan(imageBuffer)
|
|
34
|
+
|
|
35
|
+
if (result.status === 'success') {
|
|
36
|
+
switch (result.documentType) {
|
|
37
|
+
case 'passport':
|
|
38
|
+
console.log(result.fields?.passportNumber)
|
|
39
|
+
break
|
|
40
|
+
case 'pan':
|
|
41
|
+
console.log(result.panFields?.panNumber)
|
|
42
|
+
break
|
|
43
|
+
case 'aadhaar':
|
|
44
|
+
console.log(result.aadhaarFields?.aadhaarLast4)
|
|
45
|
+
break
|
|
46
|
+
case 'nrega_job_card':
|
|
47
|
+
console.log(result.nregaJobCardFields?.jobCardNumber)
|
|
48
|
+
break
|
|
49
|
+
case 'npr_letter':
|
|
50
|
+
console.log(result.nprLetterFields?.referenceNumber)
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await ocr.stop()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Local mode starts the bundled FastAPI process on `127.0.0.1`, reuses it across
|
|
59
|
+
scans, and stops it when `ocr.stop()` is called. Documents stay on the local
|
|
60
|
+
machine.
|
|
61
|
+
|
|
62
|
+
`PassportOCR` remains available as a compatibility alias for `DocumentOCR`.
|
|
63
|
+
|
|
64
|
+
For non-passport results, `status: 'success'` requires the document-specific
|
|
65
|
+
minimum fields and a conservative OCR-region confidence gate.
|
|
66
|
+
`identifierValid` reports only a local format/checksum check, not authenticity;
|
|
67
|
+
`missingRequiredFields` explains partial failures. In HTTP mode these
|
|
68
|
+
structured partial results use status `422` and are returned by the client
|
|
69
|
+
rather than thrown.
|
|
70
|
+
|
|
71
|
+
Set `DOCUMENT_OCR_KYC_LANGS=en,devanagari` (or `latin`, `ka`, `ta`, `te`) to
|
|
72
|
+
run known recognition models on the non-passport path. Each additional model
|
|
73
|
+
adds latency and memory, so choose the list from measured language slices.
|
|
74
|
+
Unsupported names or more than four models fail server readiness.
|
|
75
|
+
|
|
76
|
+
## Other modes
|
|
77
|
+
|
|
78
|
+
### HTTP
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const ocr = new DocumentOCR({
|
|
82
|
+
mode: 'http',
|
|
83
|
+
endpoint: 'https://ocr.example.com',
|
|
84
|
+
apiKey: process.env.API_TOKEN,
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`apiKey` is sent as a Bearer token. The included Python server enforces it when
|
|
89
|
+
`API_TOKEN` is configured.
|
|
90
|
+
|
|
91
|
+
### AWS Lambda
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const ocr = new DocumentOCR({
|
|
95
|
+
mode: 'lambda',
|
|
96
|
+
functionName: 'document-ocr-prod',
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Lambda mode uses IAM-authenticated direct invocation through the AWS SDK.
|
|
101
|
+
|
|
102
|
+
## Options
|
|
103
|
+
|
|
104
|
+
| Option | Type | Default | Description |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| `mode` | `local \| http \| lambda` | `local` | Invocation mode |
|
|
107
|
+
| `endpoint` | `string` | — | Required in HTTP mode |
|
|
108
|
+
| `functionName` | `string` | — | Required in Lambda mode |
|
|
109
|
+
| `timeoutMs` | `number` | `30000` | Per-attempt timeout |
|
|
110
|
+
| `retries` | `number` | `2` | Retry count |
|
|
111
|
+
| `apiKey` | `string` | — | Bearer token for HTTP mode |
|
|
112
|
+
| `authHeaders` | `(url, signal) => HeadersInit \| Promise<HeadersInit>` | — | Per-attempt HTTP credentials |
|
|
113
|
+
|
|
114
|
+
Accepted image inputs are `File`, `Blob`, `Buffer`, `ArrayBuffer`, base64
|
|
115
|
+
strings, and HTTP(S) URLs.
|
|
116
|
+
|
|
117
|
+
Set `PASSPORT_OCR_SKIP_PYTHON=1` during installation to skip local Python setup
|
|
118
|
+
when only HTTP or Lambda mode is required.
|
|
119
|
+
|
|
120
|
+
## Privacy
|
|
121
|
+
|
|
122
|
+
HTTP and Lambda modes transmit identity documents to the configured
|
|
123
|
+
infrastructure. Protect the endpoint, avoid logging extracted personal data,
|
|
124
|
+
and define an appropriate retention policy. Never submit real identity
|
|
125
|
+
documents to public issues or test fixtures.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
[MIT](LICENSE)
|
|
130
|
+
|
|
131
|
+
## Document hints, evidence, and multiple pages
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const options = { documentType: 'us_driver_license', country: 'USA', includeEvidence: true }
|
|
135
|
+
const result = await ocr.scan(imageBuffer, options)
|
|
136
|
+
console.log(result.documentFields, result.fieldEvidence, result.checks)
|
|
137
|
+
|
|
138
|
+
// Independent documents, with one result per input.
|
|
139
|
+
const batch = await ocr.scanBatch([firstImage, secondImage], options)
|
|
140
|
+
|
|
141
|
+
// Front/back images or a PDF belonging to one document.
|
|
142
|
+
const document = await ocr.scanDocument([frontImage, backImage], options)
|
|
143
|
+
console.log(document.documentFields, document.conflicts)
|
|
144
|
+
|
|
145
|
+
const catalog = await ocr.documents()
|
|
146
|
+
console.log(catalog.documents, catalog.capabilities)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The catalog reports supported families, required fields, available extraction
|
|
150
|
+
sources, and experimental status. New families include US driver licences and
|
|
151
|
+
state IDs, passport cards, green cards, EADs, visas, I-94s and W-9s. These profiles
|
|
152
|
+
remain experimental. Bank statements, utility bills, payslips and Indian business
|
|
153
|
+
records are planned, not implemented profiles.
|
|
154
|
+
Support and required fields are defined by the server catalog; extraction does
|
|
155
|
+
not establish document authenticity.
|
|
156
|
+
|
|
157
|
+
`documentFields`, `issuingCountry`, `issuingRegion`, `fieldEvidence`, `checks`,
|
|
158
|
+
and `schemaVersion` extend the result without replacing legacy field blocks.
|
|
159
|
+
Evidence contains source text, bounding boxes, source type, confidence, and an
|
|
160
|
+
optional page number. Include it only when needed because it contains document
|
|
161
|
+
values. Grouped results expose conflicting page values in `conflicts` with
|
|
162
|
+
1-based page numbers. Batch/group/catalog methods use local or HTTP mode.
|
|
163
|
+
|
|
164
|
+
## Short-lived HTTP credentials
|
|
165
|
+
|
|
166
|
+
Provide an asynchronous `authHeaders` callback to refresh headers on every
|
|
167
|
+
attempt. It receives the request URL and attempt abort signal. Returned headers
|
|
168
|
+
override the static `apiKey` header, which otherwise remains a fallback.
|
|
169
|
+
|
|
170
|
+
For Cloud Run IAM, install `google-auth-library` in the application and obtain
|
|
171
|
+
an audience-matched ID-token client using Application Default Credentials, as
|
|
172
|
+
described in the [Google Auth Library documentation](https://github.com/googleapis/google-auth-library-nodejs#working-with-id-tokens):
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { GoogleAuth } from 'google-auth-library'
|
|
176
|
+
|
|
177
|
+
const endpoint = 'https://your-private-service.run.app'
|
|
178
|
+
const identity = await new GoogleAuth().getIdTokenClient(endpoint)
|
|
179
|
+
const ocr = new DocumentOCR({
|
|
180
|
+
mode: 'http',
|
|
181
|
+
endpoint,
|
|
182
|
+
authHeaders: async () => {
|
|
183
|
+
const headers = await identity.getRequestHeaders()
|
|
184
|
+
return { Authorization: headers.get('authorization')! }
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The caller needs Cloud Run Invoker permission. API bearer-token authentication
|
|
190
|
+
on your own server continues to use `apiKey`.
|
|
191
|
+
|
|
192
|
+
## Larger Lambda documents
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const result = await ocr.scanS3({
|
|
196
|
+
bucket: 'private-document-inputs',
|
|
197
|
+
key: 'uploads/opaque-sample.pdf',
|
|
198
|
+
versionId: 'optional-object-version',
|
|
199
|
+
}, { documentType: 'us_w9', country: 'USA' })
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`scanS3` requires Lambda mode. The Lambda deployment restricts readable S3
|
|
203
|
+
buckets/prefixes and its execution role must be authorized to read the object.
|
|
204
|
+
Direct image invocations count the exact UTF-8 JSON payload, including base64
|
|
205
|
+
and options, against Lambda's 6 MiB synchronous request ceiling. Oversized
|
|
206
|
+
requests and invalid local input fail immediately without retries; use S3 for
|
|
207
|
+
larger images. Transient transport errors retain per-attempt retry/timeouts.
|
|
208
|
+
|
|
209
|
+
## Scaffold and deploy a service
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
DOCUMENT_OCR_SKIP_PYTHON=1 npx document-ocr init --target cloudrun --directory my-ocr --project YOUR_PROJECT
|
|
213
|
+
cd my-ocr
|
|
214
|
+
npx document-ocr doctor
|
|
215
|
+
npx document-ocr deploy --dry-run
|
|
216
|
+
npx document-ocr deploy
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Targets are `cloudrun`, `lambda`, `cloudflare`, and `server`. Initialization
|
|
220
|
+
copies the versioned Python source, dependency lock, deployment templates,
|
|
221
|
+
and Terraform templates into the chosen directory and writes `dococr.json`.
|
|
222
|
+
It refuses to replace existing paths. `deploy --dry-run` prints the wrapper
|
|
223
|
+
command without contacting a provider; `deploy` executes that wrapper.
|
|
224
|
+
|
|
225
|
+
`dococr.json` stores nonsecret deployment settings: target, service name,
|
|
226
|
+
`economy`/`warm` profile, OCR languages, and optional project, region, image,
|
|
227
|
+
domain, bucket, and prefix. Credentials stay in your environment/provider CLI.
|
|
228
|
+
Cloud Run uses `GCP_PROJECT` and optional `GCP_REGION`; Lambda uses `AWS_REGION`
|
|
229
|
+
and optional S3 scope; server hosting requires `DOMAIN` and `API_TOKEN`.
|
|
230
|
+
Cloudflare uses its logged-in Wrangler account and separately configured
|
|
231
|
+
`API_TOKEN` secret. See the scaffolded `HOSTING.md` for provider-specific setup.
|
|
232
|
+
|
|
233
|
+
`doctor` checks local CLI/config prerequisites. Add `--endpoint URL` for a
|
|
234
|
+
liveness request to `/health`, or `--json` for a machine-readable report. It does
|
|
235
|
+
not prove model readiness, credentials, or provider permissions. Deployment
|
|
236
|
+
wrappers validate provider setup when executed. Server deployment runs on the
|
|
237
|
+
current machine or the configured Docker remote context.
|
|
238
|
+
|
|
239
|
+
Set `DOCUMENT_OCR_SKIP_PYTHON=1` when installing solely for remote clients or
|
|
240
|
+
hosting commands; `PASSPORT_OCR_SKIP_PYTHON=1` remains supported. Local OCR needs
|
|
241
|
+
the bundled Python runtime installed normally. Runtime dependencies are installed
|
|
242
|
+
against the included versioned dependency lock.
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
## Persistent background jobs
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
const queued = await ocr.enqueueJob([frontImage, backImage], {
|
|
249
|
+
documentType: 'us_driver_license', grouped: true, notify: false,
|
|
250
|
+
})
|
|
251
|
+
const progress = await ocr.job(queued.id)
|
|
252
|
+
if (progress.status === 'succeeded') console.log(progress.result)
|
|
253
|
+
await ocr.deleteJob(queued.id)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Jobs require an HTTP/local server with persistent job storage enabled; check
|
|
257
|
+
`catalog.capabilities.jobs`. Submitting a job does not retry automatically,
|
|
258
|
+
because a lost response could otherwise create duplicate persistent work.
|
|
259
|
+
Polling and deletion use normal transport retries. Job timestamps are Unix
|
|
260
|
+
seconds; results expire according to server retention. Notifications occur only
|
|
261
|
+
when `notify: true` and the server has an enabled notification integration.
|