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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* postinstall script for document-ocr
|
|
5
|
+
*
|
|
6
|
+
* Sets up a Python virtual environment and installs dependencies
|
|
7
|
+
* required for local document OCR processing.
|
|
8
|
+
*
|
|
9
|
+
* Uses uv to install a compatible Python version (3.14) if the system
|
|
10
|
+
* Python is a different version or missing.
|
|
11
|
+
*
|
|
12
|
+
* Exits 0 even on failure — errors will surface on first scan() call.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execFileSync } from 'node:child_process'
|
|
16
|
+
import { existsSync, rmSync, writeFileSync } from 'node:fs'
|
|
17
|
+
import { join, dirname } from 'node:path'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const packageDir = join(__dirname, '..')
|
|
22
|
+
const pythonDir = join(packageDir, 'python')
|
|
23
|
+
const venvDir = join(packageDir, '.venv')
|
|
24
|
+
const markerFile = join(venvDir, '.setup-complete')
|
|
25
|
+
const constraints = join(pythonDir, 'requirements.lock')
|
|
26
|
+
|
|
27
|
+
// Python version used by the bundled runtime
|
|
28
|
+
const TARGET_PYTHON = '3.14'
|
|
29
|
+
|
|
30
|
+
function log(msg) {
|
|
31
|
+
process.stderr.write(`[document-ocr] ${msg}\n`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function run(cmd, args, opts = {}) {
|
|
35
|
+
log(`> ${cmd} ${args.join(' ')}`)
|
|
36
|
+
return execFileSync(cmd, args, {
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
|
+
timeout: 600000, // 10 minutes (OCR dependencies are large)
|
|
40
|
+
...opts,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hasUv() {
|
|
45
|
+
try {
|
|
46
|
+
const version = execFileSync('uv', ['--version'], { encoding: 'utf-8', timeout: 10000 }).trim()
|
|
47
|
+
log(`Found ${version}`)
|
|
48
|
+
return true
|
|
49
|
+
} catch {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function findCompatiblePython() {
|
|
55
|
+
/**
|
|
56
|
+
* Find a Python supported by the bundled runtime (3.14).
|
|
57
|
+
* Check versioned commands first, then generic ones.
|
|
58
|
+
*/
|
|
59
|
+
const candidates = ['python3.14', 'python3', 'python']
|
|
60
|
+
|
|
61
|
+
for (const cmd of candidates) {
|
|
62
|
+
try {
|
|
63
|
+
const version = execFileSync(cmd, ['--version'], { encoding: 'utf-8', timeout: 10000 }).trim()
|
|
64
|
+
const match = version.match(/Python (\d+)\.(\d+)/)
|
|
65
|
+
if (match) {
|
|
66
|
+
const major = parseInt(match[1], 10)
|
|
67
|
+
const minor = parseInt(match[2], 10)
|
|
68
|
+
if (major === 3 && minor === 14) {
|
|
69
|
+
log(`Found compatible ${version} via "${cmd}"`)
|
|
70
|
+
return cmd
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch { /* not found */ }
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function main() {
|
|
79
|
+
// Skip if already set up
|
|
80
|
+
if (existsSync(markerFile)) {
|
|
81
|
+
log('Python environment already set up.')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (process.env.DOCUMENT_OCR_SKIP_PYTHON === '1' || process.env.PASSPORT_OCR_SKIP_PYTHON === '1') {
|
|
86
|
+
log('Skipping Python setup (DOCUMENT_OCR_SKIP_PYTHON / PASSPORT_OCR_SKIP_PYTHON)')
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!existsSync(pythonDir) || !existsSync(constraints)) {
|
|
91
|
+
log('Warning: bundled Python runtime or dependency lock is missing. Local mode will not work.')
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const uvAvailable = hasUv()
|
|
96
|
+
|
|
97
|
+
// Strategy 1: Use uv to create venv with a pinned Python version
|
|
98
|
+
// uv can auto-download Python 3.14 even if the system has another version
|
|
99
|
+
if (uvAvailable && !existsSync(venvDir)) {
|
|
100
|
+
log(`Creating virtual environment with Python ${TARGET_PYTHON} via uv...`)
|
|
101
|
+
try {
|
|
102
|
+
run('uv', ['venv', '--python', TARGET_PYTHON, venvDir])
|
|
103
|
+
log('Installing Python dependencies...')
|
|
104
|
+
const venvPython = process.platform === 'win32'
|
|
105
|
+
? join(venvDir, 'Scripts', 'python.exe')
|
|
106
|
+
: join(venvDir, 'bin', 'python')
|
|
107
|
+
run('uv', ['pip', 'install', '--python', venvPython, '-c', constraints, '-e', pythonDir])
|
|
108
|
+
writeFileSync(markerFile, new Date().toISOString())
|
|
109
|
+
log('Setup complete.')
|
|
110
|
+
return
|
|
111
|
+
} catch (err) {
|
|
112
|
+
log(`uv setup failed: ${err.message?.split('\n')[0]}`)
|
|
113
|
+
log('Trying fallback...')
|
|
114
|
+
// Clean up failed venv
|
|
115
|
+
try { rmSync(venvDir, { recursive: true, force: true }) } catch {}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Strategy 2: Use a compatible system Python directly
|
|
120
|
+
const pythonCmd = findCompatiblePython()
|
|
121
|
+
if (pythonCmd) {
|
|
122
|
+
if (!existsSync(venvDir)) {
|
|
123
|
+
log(`Creating virtual environment with ${pythonCmd}...`)
|
|
124
|
+
try {
|
|
125
|
+
run(pythonCmd, ['-m', 'venv', venvDir])
|
|
126
|
+
} catch (err) {
|
|
127
|
+
log(`Warning: Failed to create venv: ${err.message?.split('\n')[0]}`)
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const venvPython = process.platform === 'win32'
|
|
133
|
+
? join(venvDir, 'Scripts', 'python.exe')
|
|
134
|
+
: join(venvDir, 'bin', 'python')
|
|
135
|
+
|
|
136
|
+
if (!existsSync(venvPython)) {
|
|
137
|
+
log(`Warning: venv python not found at ${venvPython}`)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log('Installing Python dependencies...')
|
|
142
|
+
try {
|
|
143
|
+
if (uvAvailable) {
|
|
144
|
+
run('uv', ['pip', 'install', '--python', venvPython, '-c', constraints, '-e', pythonDir])
|
|
145
|
+
} else {
|
|
146
|
+
run(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip'])
|
|
147
|
+
run(venvPython, ['-m', 'pip', 'install', '-c', constraints, '-e', pythonDir])
|
|
148
|
+
}
|
|
149
|
+
} catch (err) {
|
|
150
|
+
log(`Warning: Failed to install dependencies: ${err.message?.split('\n')[0]}`)
|
|
151
|
+
log('Retry with: npm rebuild document-ocr')
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
writeFileSync(markerFile, new Date().toISOString())
|
|
156
|
+
log('Setup complete.')
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Strategy 3: Nothing worked
|
|
161
|
+
log('Warning: Could not set up Python environment.')
|
|
162
|
+
log('The bundled runtime requires Python 3.14.')
|
|
163
|
+
if (!uvAvailable) {
|
|
164
|
+
log('Install uv to auto-download a compatible Python:')
|
|
165
|
+
log(' curl -LsSf https://astral.sh/uv/install.sh | sh')
|
|
166
|
+
}
|
|
167
|
+
log('Then retry: npm rebuild document-ocr')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main().catch((err) => {
|
|
171
|
+
log(`Warning: Setup failed: ${err.message}`)
|
|
172
|
+
process.exit(0)
|
|
173
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Bundle only canonical source/templates, never local credentials or build output. */
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, lstatSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { dirname, extname, join, resolve } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
+
const repo = resolve(packageDir, '../..')
|
|
8
|
+
const destination = join(packageDir, 'hosting')
|
|
9
|
+
const extensions = new Set(['.py', '.html', '.toml', '.yaml', '.yml', '.json', '.jsonc', '.ts', '.js', '.mjs', '.sh', '.tf', '.hcl', '.md'])
|
|
10
|
+
const names = new Set(['Dockerfile', 'Dockerfile.lambda', 'Caddyfile', 'requirements.lock', 'terraform.tfvars.example', 'LICENSE', '.dockerignore', '.gitignore'])
|
|
11
|
+
const excluded = new Set(['node_modules', '__pycache__', '.terraform', '.wrangler', '.aws-sam', 'dist'])
|
|
12
|
+
|
|
13
|
+
function allowed(source) {
|
|
14
|
+
const name = source.split('/').at(-1)
|
|
15
|
+
if (excluded.has(name) || name.startsWith('.env')) return false
|
|
16
|
+
const details = lstatSync(source)
|
|
17
|
+
if (details.isSymbolicLink()) return false
|
|
18
|
+
if (details.isDirectory()) return !name.startsWith('.')
|
|
19
|
+
return names.has(name) || (!name.startsWith('.') && extensions.has(extname(name)))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
rmSync(destination, { recursive: true, force: true })
|
|
23
|
+
mkdirSync(destination, { recursive: true })
|
|
24
|
+
for (const name of ['core', 'deploy', 'infra']) {
|
|
25
|
+
if (existsSync(join(repo, name))) cpSync(join(repo, name), join(destination, name), { recursive: true, filter: allowed })
|
|
26
|
+
}
|
|
27
|
+
for (const name of ['pyproject.toml', 'requirements.lock', 'README.md', 'HOSTING.md', 'FEATURES.md', 'LICENSE', '.dockerignore', '.gcloudignore']) {
|
|
28
|
+
if (existsSync(join(repo, name))) cpSync(join(repo, name), join(destination, name))
|
|
29
|
+
}
|
|
30
|
+
// A scaffolding build context must never upload a developer's credentials or data.
|
|
31
|
+
if (!existsSync(join(destination, '.dockerignore'))) {
|
|
32
|
+
writeFileSync(join(destination, '.dockerignore'), '**\n!core/\n!core/**\n!deploy/\n!deploy/docker/\n!deploy/docker/server.py\n!deploy/lambda/\n!deploy/lambda/handler.py\n!requirements.lock\n**/__pycache__\n')
|
|
33
|
+
}
|
|
34
|
+
writeFileSync(join(destination, '.gitignore'), '.env*\n*.pem\n*.key\n*.tfvars\n*.tfstate*\n.venv/\n**/node_modules/\n**/__pycache__/\n**/.terraform/\n**/.wrangler/\n**/.aws-sam/\nbenchmark-data/\nsample-passports/\n')
|
|
35
|
+
console.log(`[sync-hosting] Bundled ${readdirSync(destination).length} source/template entries`)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Sync canonical Python source into the npm package for bundling.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
PACKAGE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
7
|
+
REPO_ROOT="$(cd "$PACKAGE_DIR/../.." && pwd)"
|
|
8
|
+
|
|
9
|
+
# Copy core/ (the pipeline)
|
|
10
|
+
rm -rf "$PACKAGE_DIR/python/core"
|
|
11
|
+
cp -r "$REPO_ROOT/core" "$PACKAGE_DIR/python/core"
|
|
12
|
+
find "$PACKAGE_DIR/python" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
13
|
+
|
|
14
|
+
# Copy the canonical runtime dependency constraints.
|
|
15
|
+
cp "$REPO_ROOT/requirements.lock" "$PACKAGE_DIR/python/requirements.lock"
|
|
16
|
+
|
|
17
|
+
# Copy server.py
|
|
18
|
+
cp "$REPO_ROOT/deploy/docker/server.py" "$PACKAGE_DIR/python/server.py"
|
|
19
|
+
|
|
20
|
+
echo "[sync-python] Synced core/ and server.py into packages/passport-ocr/python/"
|