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 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/image.ts","../src/retry.ts","../src/local-server.ts","../src/client.ts"],"sourcesContent":["export { DocumentOCR, PassportOCR } from './client'\nexport { LocalServer, getLocalServer } from './local-server'\nexport type {\n PassportFields,\n BackPageFields,\n PanFields,\n AadhaarFields,\n DrivingLicenceFields,\n VoterIdFields,\n NregaMember,\n NregaJobCardFields,\n NprLetterFields,\n DocumentScanResult,\n DocumentOCROptions,\n PassportScanResult,\n PassportOCROptions,\n ImageInput,\n ClientMode,\n DocumentStatus,\n DocumentType,\n PageType,\n UnsupportedReason,\n ScanOptions,\n S3ImageInput,\n BatchScanResult,\n DocumentScanGroupResult,\n JobOptions,\n OCRJob,\n DocumentDefinition,\n DocumentCatalog,\n FieldEvidence,\n JsonValue,\n} from './types'\nexport { HttpError, InputValidationError } from './errors'\n","/** Preserve transport status separately from the server's human-readable message. */\nexport class HttpError extends Error {\n constructor(readonly status: number, message: string) {\n super(message)\n this.name = 'HttpError'\n }\n\n get retryable(): boolean {\n return this.status === 408 || this.status === 429 || this.status >= 500\n }\n}\n\n/** Invalid client input cannot succeed by retrying the same request. */\nexport class InputValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'InputValidationError'\n }\n}\n","import type { ImageInput } from './types'\nimport { HttpError, InputValidationError } from './errors'\n\nfunction isHttpUrl(input: string): boolean {\n return /^https?:\\/\\//i.test(input)\n}\n\nasync function fetchImage(url: string, signal?: AbortSignal): Promise<Response> {\n const res = await fetch(url, { signal })\n if (!res.ok) {\n await res.body?.cancel()\n throw new HttpError(res.status, `Failed to fetch image from URL: ${res.status}`)\n }\n return res\n}\n\n/**\n * Normalise any supported image input to a base64 string.\n */\nexport async function normalizeToBase64(input: ImageInput, signal?: AbortSignal): Promise<string> {\n // Already a base64 string\n if (typeof input === 'string' && !isHttpUrl(input)) {\n validateBase64(input)\n return input\n }\n\n // URL — fetch first\n if (typeof input === 'string' && isHttpUrl(input)) {\n const res = await fetchImage(input, signal)\n const buf = await res.arrayBuffer()\n return arrayBufferToBase64(buf)\n }\n\n // Buffer (Node.js)\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {\n return input.toString('base64')\n }\n\n // ArrayBuffer\n if (input instanceof ArrayBuffer) {\n return arrayBufferToBase64(input)\n }\n\n // Blob / File\n if (typeof Blob !== 'undefined' && input instanceof Blob) {\n const buf = await input.arrayBuffer()\n return arrayBufferToBase64(buf)\n }\n\n throw new InputValidationError('Unsupported image input type')\n}\n\n/**\n * Normalise any supported image input to a Blob for FormData upload.\n */\nexport async function normalizeToBlob(input: ImageInput, signal?: AbortSignal): Promise<Blob> {\n // Blob / File\n if (typeof Blob !== 'undefined' && input instanceof Blob) {\n if (input.type.startsWith('image/') || input.type === 'application/pdf') return input\n const header = new Uint8Array(await input.slice(0, 16).arrayBuffer())\n return new Blob([input], { type: mediaType(header) })\n }\n\n // Buffer (Node.js) — copy to a fresh ArrayBuffer-backed Uint8Array. This\n // avoids ReadableStream issues in Node 24+, only copies the Buffer's own\n // window (respecting byteOffset/byteLength on a subarray view), and yields a\n // BlobPart typed over ArrayBuffer (not the SharedArrayBuffer-inclusive\n // ArrayBufferLike that `input.buffer` widens to).\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {\n const copy = new Uint8Array(input)\n return new Blob([copy], { type: mediaType(copy) })\n }\n\n // ArrayBuffer\n if (input instanceof ArrayBuffer) {\n return new Blob([input], { type: mediaType(new Uint8Array(input)) })\n }\n\n // URL — fetch\n if (typeof input === 'string' && isHttpUrl(input)) {\n const res = await fetchImage(input, signal)\n return normalizeToBlob(await res.blob(), signal)\n }\n\n // Base64 string\n if (typeof input === 'string') {\n validateBase64(input)\n const binary = atob(input)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return new Blob([bytes], { type: mediaType(bytes) })\n }\n\n throw new InputValidationError('Unsupported image input type')\n}\n\nfunction validateBase64(value: string): void {\n if (!value || !/^[A-Za-z0-9+/]*={0,2}$/.test(value) || value.length % 4 === 1\n || (value.includes('=') && value.length % 4 !== 0)) {\n throw new InputValidationError('Image string must be base64 or an HTTP(S) URL')\n }\n}\n\nfunction mediaType(bytes: Uint8Array): string {\n const header = String.fromCharCode(...bytes.subarray(0, 16))\n if (header.startsWith('%PDF-')) return 'application/pdf'\n if (header.startsWith('\\x89PNG\\r\\n\\x1a\\n')) return 'image/png'\n if (header.startsWith('GIF87a') || header.startsWith('GIF89a')) return 'image/gif'\n if (header.startsWith('II*\\x00') || header.startsWith('MM\\x00*')) return 'image/tiff'\n if (header.startsWith('RIFF') && header.slice(8, 12) === 'WEBP') return 'image/webp'\n if (header.startsWith('BM')) return 'image/bmp'\n if (header.slice(4, 8) === 'ftyp') return header.slice(8, 12) === 'avif' ? 'image/avif' : 'image/heif'\n // Preserve the historic Buffer behavior for other image encodings; the\n // server validates actual bytes. Untyped multipart blobs are rejected there.\n return 'image/jpeg'\n}\n\nfunction arrayBufferToBase64(buffer: ArrayBuffer): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(buffer).toString('base64')\n }\n // Browser\n const bytes = new Uint8Array(buffer)\n let binary = ''\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i])\n }\n return btoa(binary)\n}\n","import { HttpError, InputValidationError } from './errors'\n\nexport interface RetryOptions {\n retries: number\n timeoutMs: number\n}\n\nexport function validateRetryOptions({ retries, timeoutMs }: RetryOptions): void {\n if (!Number.isInteger(retries) || retries < 0) {\n throw new RangeError('retries must be a non-negative integer')\n }\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {\n throw new RangeError('timeoutMs must be between 0 and 2147483647 milliseconds (exclusive of 0)')\n }\n}\n\nexport async function withRetry<T>(\n fn: (signal: AbortSignal) => Promise<T>,\n options: RetryOptions,\n): Promise<T> {\n validateRetryOptions(options)\n const { retries, timeoutMs } = options\n let lastError: Error | undefined\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n const controller = new AbortController()\n let timeout: ReturnType<typeof setTimeout> | undefined\n const deadline = new Promise<never>((_, reject) => {\n timeout = setTimeout(() => {\n const error = new Error(`OCR request timed out after ${timeoutMs}ms`)\n error.name = 'TimeoutError'\n reject(error)\n controller.abort(error)\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([fn(controller.signal), deadline])\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err))\n\n if (lastError instanceof InputValidationError || (lastError instanceof HttpError && !lastError.retryable)) {\n throw lastError\n }\n\n } finally {\n clearTimeout(timeout)\n }\n\n if (attempt < retries) {\n // Exponential backoff: 500ms, 1s, 2s...\n const delay = Math.min(500 * Math.pow(2, attempt), 5000)\n await new Promise((r) => setTimeout(r, delay))\n }\n }\n\n throw lastError ?? new Error('All retries exhausted')\n}\n","import { type ChildProcess, spawn } from 'node:child_process'\nimport { createServer } from 'node:net'\nimport { existsSync } from 'node:fs'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { setTimeout as delay } from 'node:timers/promises'\n\n// Resolve package directory — works for both ESM and CJS builds\nfunction resolvePackageDir(): string {\n // In ESM context, use import.meta.url\n // In CJS context, use __dirname\n try {\n if (typeof import.meta?.url === 'string') {\n // dist/index.js -> package root\n return join(dirname(fileURLToPath(import.meta.url)), '..')\n }\n } catch {\n // fallback\n }\n // CJS fallback: __dirname is dist/, go up one level\n if (typeof __dirname === 'string') {\n return join(__dirname, '..')\n }\n throw new Error('Cannot resolve document-ocr package directory')\n}\n\nconst HEALTH_POLL_INTERVAL_MS = 500\nconst HEALTH_TIMEOUT_MS = 120_000 // 120s for first-time model download\n\n/**\n * Finds a free port by briefly binding to port 0.\n */\nfunction findFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const srv = createServer()\n srv.listen(0, '127.0.0.1', () => {\n const addr = srv.address()\n if (addr && typeof addr === 'object') {\n const port = addr.port\n srv.close(() => resolve(port))\n } else {\n srv.close(() => reject(new Error('Could not determine free port')))\n }\n })\n srv.on('error', reject)\n })\n}\n\n/**\n * Manages a local Python FastAPI server for passport OCR.\n *\n * Uses a module-level singleton so multiple PassportOCR instances\n * share a single server process.\n */\nexport class LocalServer {\n private process: ChildProcess | null = null\n private ready: Promise<string> | null = null\n private startupAbort: AbortController | null = null\n private cleanupHandler: (() => void) | null = null\n\n /**\n * Ensure the local Python server is running and return its endpoint URL.\n */\n async ensureRunning(): Promise<string> {\n // If already starting or running, return the existing promise\n if (this.ready) {\n return this.ready\n }\n\n const controller = new AbortController()\n this.startupAbort = controller\n const ready = this._start(controller)\n this.ready = ready\n\n // If start fails, clear the promise so we can retry\n ready.catch(() => {\n if (this.ready === ready) this.ready = null\n })\n\n return ready\n }\n\n private async _start(controller: AbortController): Promise<string> {\n const packageDir = resolvePackageDir()\n const pythonDir = join(packageDir, 'python')\n const venvDir = join(packageDir, '.venv')\n\n // Check that setup was completed\n const markerFile = join(venvDir, '.setup-complete')\n if (!existsSync(markerFile)) {\n throw new Error(\n 'document-ocr: Python environment not set up. ' +\n 'Run \"npm rebuild document-ocr\" or ensure Python 3.14 and uv are installed. ' +\n 'Check that the postinstall script ran successfully.',\n )\n }\n\n // Find the venv python executable\n const isWindows = process.platform === 'win32'\n const pythonExe = isWindows\n ? join(venvDir, 'Scripts', 'python.exe')\n : join(venvDir, 'bin', 'python')\n\n if (!existsSync(pythonExe)) {\n throw new Error(\n `document-ocr: Python executable not found at ${pythonExe}. ` +\n 'Run \"npm rebuild document-ocr\" to re-create the virtual environment.',\n )\n }\n\n // Find a free port\n const port = await findFreePort()\n controller.signal.throwIfAborted()\n const endpoint = `http://127.0.0.1:${port}`\n\n // Spawn the uvicorn server\n const args = [\n '-m',\n 'uvicorn',\n 'server:app',\n '--host',\n '127.0.0.1',\n '--port',\n String(port),\n '--log-level',\n 'warning',\n ]\n\n const child = spawn(pythonExe, args, {\n cwd: pythonDir,\n env: {\n ...process.env,\n PYTHONPATH: pythonDir,\n PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK: 'True',\n // Suppress paddle logging noise\n GLOG_minloglevel: '2',\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n // Detach on Windows to avoid blocking\n detached: isWindows,\n })\n this.process = child\n\n // Drain child output so startup can't deadlock on a full pipe buffer.\n let processLogs = ''\n const appendLogs = (chunk: Buffer) => {\n processLogs += chunk.toString()\n if (processLogs.length > 4096) {\n processLogs = processLogs.slice(-4096)\n }\n }\n\n child.stdout?.on('data', appendLogs)\n child.stderr?.on('data', appendLogs)\n\n const cleanup = () => child.kill('SIGTERM')\n this.cleanupHandler = cleanup\n process.once('exit', cleanup)\n\n // Handle unexpected exit\n const exitPromise = new Promise<never>((_, reject) => {\n const failed = (error: Error) => {\n process.removeListener('exit', cleanup)\n if (this.cleanupHandler === cleanup) this.cleanupHandler = null\n if (this.process === child) {\n this.ready = null\n this.process = null\n }\n controller.abort(error)\n reject(error)\n }\n child.once('error', failed)\n child.once('exit', (code, signal) => failed(new Error(\n `document-ocr: Python server exited (code=${code}, signal=${signal}).\\n${processLogs}`,\n )))\n })\n\n try {\n return await Promise.race([this._waitForReady(endpoint, controller.signal), exitPromise])\n } catch (error) {\n if (this.process === child) await this.stop()\n throw error\n } finally {\n if (controller.signal.aborted) process.removeListener('exit', cleanup)\n }\n }\n\n private async _waitForReady(endpoint: string, signal?: AbortSignal): Promise<string> {\n const readinessUrl = `${endpoint}/ready`\n const startTime = Date.now()\n\n while (Date.now() - startTime < HEALTH_TIMEOUT_MS) {\n signal?.throwIfAborted()\n let terminalFailure: string | null = null\n const probeController = new AbortController()\n const cancelProbe = () => probeController.abort(signal?.reason)\n signal?.addEventListener('abort', cancelProbe, { once: true })\n const timeout = setTimeout(() => probeController.abort(), 2000)\n try {\n const res = await fetch(readinessUrl, {\n signal: probeController.signal,\n })\n const body = (await res.json().catch(() => null)) as {\n status?: unknown\n error?: unknown\n } | null\n signal?.throwIfAborted()\n if (res.ok && body?.status === 'ready') {\n return endpoint\n }\n if (body?.status === 'model_init_failed') {\n const detail =\n typeof body.error === 'string'\n ? body.error.replace(/\\s+/g, ' ').slice(0, 500)\n : 'configured OCR model initialization failed'\n terminalFailure = detail\n }\n } catch {\n signal?.throwIfAborted()\n // Server not ready yet, keep polling\n } finally {\n clearTimeout(timeout)\n signal?.removeEventListener('abort', cancelProbe)\n }\n\n if (terminalFailure) {\n throw new Error(\n `document-ocr: OCR model initialization failed: ${terminalFailure}`,\n )\n }\n\n await delay(HEALTH_POLL_INTERVAL_MS, undefined, { signal })\n }\n\n // Timed out\n throw new Error(\n `document-ocr: Python server did not become ready within ${HEALTH_TIMEOUT_MS / 1000}s. ` +\n 'This might happen on first run while models are being downloaded.',\n )\n }\n\n /**\n * Stop the local Python server if running.\n */\n async stop(): Promise<void> {\n if (this.cleanupHandler) {\n process.removeListener('exit', this.cleanupHandler)\n this.cleanupHandler = null\n }\n this.startupAbort?.abort(new Error('document-ocr: local server stopped'))\n this.startupAbort = null\n this.ready = null\n\n if (this.process) {\n const proc = this.process\n this.process = null\n if (proc.exitCode !== null || proc.signalCode !== null) return\n await new Promise<void>((resolve) => {\n const done = () => {\n clearTimeout(forceKill)\n clearTimeout(deadline)\n proc.removeListener('exit', done)\n proc.removeListener('error', done)\n resolve()\n }\n const forceKill = setTimeout(() => {\n try { proc.kill('SIGKILL') } catch { done() }\n }, 5000)\n const deadline = setTimeout(done, 6000)\n proc.once('exit', done)\n proc.once('error', done)\n try {\n if (!proc.kill('SIGTERM')) done()\n } catch {\n done()\n }\n })\n }\n }\n}\n\n// Module-level singleton — shared across all PassportOCR instances\nlet _instance: LocalServer | null = null\n\nexport function getLocalServer(): LocalServer {\n if (!_instance) {\n _instance = new LocalServer()\n }\n return _instance\n}\n","import type {\n BatchScanResult, DocumentCatalog, DocumentOCROptions, DocumentScanResult, DocumentScanGroupResult,\n ImageInput, ScanOptions, S3ImageInput, JobOptions, OCRJob,\n} from './types'\nimport { normalizeToBase64, normalizeToBlob } from './image'\nimport { validateRetryOptions, withRetry } from './retry'\nimport { HttpError, InputValidationError } from './errors'\nimport { getLocalServer } from './local-server'\n\nconst MAX_LAMBDA_PAYLOAD_BYTES = 6 * 1024 * 1024\n\nexport class DocumentOCR {\n private mode: 'local' | 'http' | 'lambda'\n private endpoint?: string\n private functionName?: string\n private timeoutMs: number\n private retries: number\n private apiKey?: string\n private authHeaders?: DocumentOCROptions['authHeaders']\n\n constructor(options: DocumentOCROptions = {}) {\n this.mode = options.mode ?? 'local'\n this.endpoint = options.endpoint\n this.functionName = options.functionName\n this.timeoutMs = options.timeoutMs ?? 30000\n this.retries = options.retries ?? 2\n this.apiKey = options.apiKey\n this.authHeaders = options.authHeaders\n validateRetryOptions({ retries: this.retries, timeoutMs: this.timeoutMs })\n if (this.mode === 'http' && !this.endpoint) throw new Error('endpoint is required for http mode')\n if (this.mode === 'lambda' && !this.functionName) throw new Error('functionName is required for lambda mode')\n if (this.mode === 'local' && this.endpoint) {\n console.warn('document-ocr: endpoint is ignored in local mode. ' +\n 'Use mode: \"http\" if you want to connect to an external server.')\n }\n }\n\n async scan(image: ImageInput, options: ScanOptions = {}): Promise<DocumentScanResult> {\n const parameters = scanParameters(options)\n return this.retry(async (signal) => {\n if (this.mode === 'lambda') {\n const base64 = await normalizeToBase64(image, signal)\n return this.invokeLambda({ image_base64: base64, ...parameters }, signal)\n }\n const form = new FormData()\n form.append('image', await normalizeToBlob(image, signal), 'document')\n appendParameters(form, parameters)\n const { status, data } = await this.request('/scan', signal, { method: 'POST', body: form })\n return parseScanResponse(status, data)\n })\n }\n\n async scanBatch(images: ImageInput[], options: ScanOptions = {}): Promise<BatchScanResult> {\n return this.uploadMany('/scan/batch', images, options)\n }\n\n async scanDocument(images: ImageInput[], options: ScanOptions = {}): Promise<DocumentScanGroupResult> {\n return this.uploadMany('/scan/document', images, options) as Promise<DocumentScanGroupResult>\n }\n\n private async uploadMany(path: string, images: ImageInput[], options: ScanOptions): Promise<BatchScanResult> {\n this.requireHttp(path === '/scan/batch' ? 'scanBatch' : 'scanDocument')\n if (!Array.isArray(images) || images.length === 0) throw new InputValidationError('scanBatch requires at least one image')\n const parameters = scanParameters(options)\n return this.retry(async (signal) => {\n const form = new FormData()\n for (const [index, image] of images.entries()) {\n form.append('images', await normalizeToBlob(image, signal), `document-${index}`)\n }\n appendParameters(form, parameters)\n const { status, data } = await this.request(path, signal, { method: 'POST', body: form })\n const body = asObject(data)\n const valid = body !== null && ['success', 'partial', 'failure'].includes(String(body.status))\n && Array.isArray(body.results) && body.results.every(isScanResult) && Array.isArray(body.errors)\n checkStatus(status, data, status === 422 && valid)\n if (!valid) throw new Error('Invalid OCR response: expected a batch scan result')\n if (path === '/scan/document' && (!asObject(body.documentFields) || !Array.isArray(body.conflicts))) {\n throw new Error('Invalid OCR response: expected grouped document fields and conflicts')\n }\n return data as BatchScanResult\n })\n }\n\n async documents(): Promise<DocumentCatalog> {\n this.requireHttp('documents')\n return this.retry(async (signal) => {\n const { status, data } = await this.request('/documents', signal)\n checkStatus(status, data)\n const body = asObject(data)\n if (!body || typeof body.schemaVersion !== 'number' || !Array.isArray(body.documents) || !asObject(body.capabilities)) {\n throw new Error('Invalid OCR response: expected a document catalog')\n }\n return data as DocumentCatalog\n })\n }\n\n async enqueueJob(images: ImageInput[], options: JobOptions = {}): Promise<OCRJob> {\n this.requireHttp('enqueueJob')\n if (!Array.isArray(images) || images.length === 0) throw new InputValidationError('enqueueJob requires at least one image')\n const parameters = scanParameters(options)\n for (const key of ['grouped', 'notify'] as const) {\n if (options[key] !== undefined) {\n if (typeof options[key] !== 'boolean') throw new InputValidationError(`${key} must be boolean`)\n parameters[key] = options[key]\n }\n }\n // Submission creates persistent work. Do not retry it automatically without\n // server-side idempotency: a lost response could otherwise enqueue twice.\n return withRetry(async (signal) => {\n const form = new FormData()\n for (const [index, image] of images.entries()) form.append('images', await normalizeToBlob(image, signal), `document-${index}`)\n appendParameters(form, parameters)\n const { status, data } = await this.request('/jobs', signal, { method: 'POST', body: form })\n checkStatus(status, data)\n return parseJob(data)\n }, { retries: 0, timeoutMs: this.timeoutMs })\n }\n\n async job(id: string): Promise<OCRJob> {\n this.requireHttp('job')\n const path = jobPath(id)\n return this.retry(async (signal) => {\n const { status, data } = await this.request(path, signal)\n checkStatus(status, data)\n return parseJob(data)\n })\n }\n\n async deleteJob(id: string): Promise<{ deleted: true }> {\n this.requireHttp('deleteJob')\n const path = jobPath(id)\n return this.retry(async (signal) => {\n const { status, data } = await this.request(path, signal, { method: 'DELETE' })\n checkStatus(status, data)\n if (asObject(data)?.deleted !== true) throw new Error('Invalid OCR response: expected deletion confirmation')\n return { deleted: true }\n })\n }\n\n async scanS3(image: S3ImageInput, options: ScanOptions = {}): Promise<DocumentScanResult> {\n if (this.mode !== 'lambda') throw new InputValidationError('scanS3 is available only in lambda mode')\n if (!image || typeof image.bucket !== 'string' || !image.bucket.trim()\n || typeof image.key !== 'string' || !image.key.trim()\n || (image.versionId !== undefined && (typeof image.versionId !== 'string' || !image.versionId))) {\n throw new InputValidationError('scanS3 requires bucket, key, and an optional non-empty versionId')\n }\n const parameters = scanParameters(options)\n const s3 = { bucket: image.bucket, key: image.key, ...(image.versionId === undefined ? {} : { version_id: image.versionId }) }\n return this.retry((signal) => this.invokeLambda({ s3, ...parameters }, signal))\n }\n\n async stop(): Promise<void> {\n if (this.mode === 'local') await getLocalServer().stop()\n }\n\n private requireHttp(method: string): void {\n if (this.mode === 'lambda') throw new InputValidationError(`${method} requires local or http mode`)\n }\n\n private retry<T>(operation: (signal: AbortSignal) => Promise<T>): Promise<T> {\n return withRetry(operation, { retries: this.retries, timeoutMs: this.timeoutMs })\n }\n\n private async request(path: string, signal: AbortSignal, init: RequestInit = {}): Promise<{ status: number, data: unknown }> {\n const endpoint = this.mode === 'local' ? await getLocalServer().ensureRunning() : this.endpoint!\n const url = `${endpoint.replace(/\\/+$/, '')}${path}`\n signal.throwIfAborted()\n const headers = new Headers(this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {})\n if (this.authHeaders) {\n new Headers(await this.authHeaders(url, signal)).forEach((value, key) => headers.set(key, value))\n }\n signal.throwIfAborted()\n const response = await fetch(url, { ...init, headers, signal })\n const data: unknown = await response.json().catch(() => null)\n return { status: response.status, data }\n }\n\n private async invokeLambda(event: Record<string, unknown>, signal: AbortSignal): Promise<DocumentScanResult> {\n const payload = JSON.stringify(event)\n if (new TextEncoder().encode(payload).byteLength > MAX_LAMBDA_PAYLOAD_BYTES) {\n throw new InputValidationError('Lambda payload exceeds 6 MiB; upload the image to S3 and use scanS3()')\n }\n signal.throwIfAborted()\n const { LambdaClient, InvokeCommand } = await import('@aws-sdk/client-lambda')\n signal.throwIfAborted()\n const client = new LambdaClient({})\n const command = new InvokeCommand({ FunctionName: this.functionName, Payload: payload })\n try {\n const response = await client.send(command, { abortSignal: signal })\n if (response.FunctionError) throw new Error(`Lambda invocation failed: ${response.FunctionError}`)\n const result = JSON.parse(new TextDecoder().decode(response.Payload))\n if (typeof result?.statusCode === 'number') {\n let body: unknown = result.body\n if (typeof body === 'string') {\n try { body = JSON.parse(body) } catch { body = null }\n }\n return parseScanResponse(result.statusCode, body)\n }\n return parseScanResponse(200, result)\n } finally {\n client.destroy()\n }\n }\n}\n\nfunction scanParameters(options: ScanOptions): Record<string, string | boolean> {\n if (!options || typeof options !== 'object') throw new InputValidationError('scan options must be an object')\n const result: Record<string, string | boolean> = {}\n for (const [key, wireKey] of [['documentType', 'document_type'], ['country', 'country']] as const) {\n const value = options[key]\n if (value !== undefined) {\n if (typeof value !== 'string' || !value.trim()) throw new InputValidationError(`${key} must be a non-empty string`)\n result[wireKey] = value\n }\n }\n if (options.includeEvidence !== undefined) {\n if (typeof options.includeEvidence !== 'boolean') throw new InputValidationError('includeEvidence must be boolean')\n result.include_evidence = options.includeEvidence\n }\n return result\n}\n\nfunction appendParameters(form: FormData, parameters: Record<string, string | boolean>): void {\n for (const [key, value] of Object.entries(parameters)) form.append(key, String(value))\n}\n\nfunction jobPath(id: string): string {\n if (typeof id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(id)) throw new InputValidationError('job id must contain letters, digits, hyphens, or underscores')\n return `/jobs/${encodeURIComponent(id)}`\n}\n\nfunction parseJob(data: unknown): OCRJob {\n const body = asObject(data)\n if (!body || typeof body.id !== 'string' || !['queued', 'running', 'succeeded', 'failed'].includes(String(body.status))\n || typeof body.createdAt !== 'number' || typeof body.expiresAt !== 'number' || typeof body.attempts !== 'number'\n || (body.error !== null && typeof body.error !== 'string')) throw new Error('Invalid OCR response: expected a job')\n return data as OCRJob\n}\n\nfunction asObject(data: unknown): Record<string, unknown> | null {\n return data !== null && typeof data === 'object' && !Array.isArray(data) ? data as Record<string, unknown> : null\n}\n\nfunction isScanResult(data: unknown): boolean {\n const body = asObject(data)\n return body !== null && ['success', 'failure', 'unsupported_page'].includes(String(body.status))\n && typeof body.documentType === 'string' && typeof body.pageType === 'string' && Array.isArray(body.errors)\n}\n\nfunction checkStatus(status: number, data: unknown, allow422 = false): void {\n if ((status >= 200 && status < 300) || allow422) return\n const body = asObject(data)\n const message = typeof body?.error === 'string' ? body.error\n : typeof body?.detail === 'string' ? body.detail : `OCR request failed: ${status}`\n throw new HttpError(status, message)\n}\n\nfunction parseScanResponse(status: number, data: unknown): DocumentScanResult {\n const valid = isScanResult(data)\n checkStatus(status, data, status === 422 && valid)\n if (!valid) throw new Error('Invalid OCR response: expected a document scan result')\n return data as DocumentScanResult\n}\n\nexport const PassportOCR = DocumentOCR\n"],"mappings":"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","names":["delay","delay"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
interface PassportFields {
|
|
2
|
+
surname: string | null;
|
|
3
|
+
givenNames: string | null;
|
|
4
|
+
fullName: string | null;
|
|
5
|
+
passportNumber: string | null;
|
|
6
|
+
nationality: string | null;
|
|
7
|
+
dateOfBirth: string | null;
|
|
8
|
+
sex: 'M' | 'F' | 'X' | null;
|
|
9
|
+
expiryDate: string | null;
|
|
10
|
+
issueDate: string | null;
|
|
11
|
+
placeOfBirth: string | null;
|
|
12
|
+
countryCode: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface BackPageFields {
|
|
15
|
+
fatherName: string | null;
|
|
16
|
+
motherName: string | null;
|
|
17
|
+
spouseName: string | null;
|
|
18
|
+
address: string | null;
|
|
19
|
+
pincode: string | null;
|
|
20
|
+
city: string | null;
|
|
21
|
+
state: string | null;
|
|
22
|
+
fileNumber: string | null;
|
|
23
|
+
oldPassportNumber: string | null;
|
|
24
|
+
oldPassportDateOfIssue: string | null;
|
|
25
|
+
oldPassportPlaceOfIssue: string | null;
|
|
26
|
+
}
|
|
27
|
+
interface PanFields {
|
|
28
|
+
panNumber: string | null;
|
|
29
|
+
name: string | null;
|
|
30
|
+
fatherName: string | null;
|
|
31
|
+
dateOfBirth: string | null;
|
|
32
|
+
}
|
|
33
|
+
interface AadhaarFields {
|
|
34
|
+
aadhaarNumber: string | null;
|
|
35
|
+
name: string | null;
|
|
36
|
+
dateOfBirth: string | null;
|
|
37
|
+
yearOfBirth: string | null;
|
|
38
|
+
gender: string | null;
|
|
39
|
+
address: string | null;
|
|
40
|
+
pincode: string | null;
|
|
41
|
+
checksumValid: boolean;
|
|
42
|
+
aadhaarMasked: boolean;
|
|
43
|
+
aadhaarLast4: string | null;
|
|
44
|
+
vid: string | null;
|
|
45
|
+
}
|
|
46
|
+
interface DrivingLicenceFields {
|
|
47
|
+
dlNumber: string | null;
|
|
48
|
+
name: string | null;
|
|
49
|
+
dateOfBirth: string | null;
|
|
50
|
+
issueDate: string | null;
|
|
51
|
+
validityDate: string | null;
|
|
52
|
+
address: string | null;
|
|
53
|
+
relationName: string | null;
|
|
54
|
+
bloodGroup: string | null;
|
|
55
|
+
classOfVehicle: string | null;
|
|
56
|
+
validityDateTransport: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface VoterIdFields {
|
|
59
|
+
epicNumber: string | null;
|
|
60
|
+
name: string | null;
|
|
61
|
+
relationName: string | null;
|
|
62
|
+
relationType: string | null;
|
|
63
|
+
gender: string | null;
|
|
64
|
+
dateOfBirth: string | null;
|
|
65
|
+
age: string | null;
|
|
66
|
+
}
|
|
67
|
+
interface NregaMember {
|
|
68
|
+
serialNumber: string | null;
|
|
69
|
+
name: string | null;
|
|
70
|
+
fatherOrHusbandName: string | null;
|
|
71
|
+
gender: 'MALE' | 'FEMALE' | 'TRANSGENDER' | null;
|
|
72
|
+
age: number | null;
|
|
73
|
+
}
|
|
74
|
+
interface NregaJobCardFields {
|
|
75
|
+
jobCardNumber: string | null;
|
|
76
|
+
headOfHousehold: string | null;
|
|
77
|
+
category: string | null;
|
|
78
|
+
registrationDate: string | null;
|
|
79
|
+
validityFrom: string | null;
|
|
80
|
+
validityTo: string | null;
|
|
81
|
+
address: string | null;
|
|
82
|
+
village: string | null;
|
|
83
|
+
gramPanchayat: string | null;
|
|
84
|
+
block: string | null;
|
|
85
|
+
district: string | null;
|
|
86
|
+
state: string | null;
|
|
87
|
+
bplStatus: boolean | null;
|
|
88
|
+
familyId: string | null;
|
|
89
|
+
members: NregaMember[];
|
|
90
|
+
}
|
|
91
|
+
interface NprLetterFields {
|
|
92
|
+
referenceNumber: string | null;
|
|
93
|
+
name: string | null;
|
|
94
|
+
address: string | null;
|
|
95
|
+
pincode: string | null;
|
|
96
|
+
issueDate: string | null;
|
|
97
|
+
}
|
|
98
|
+
type DocumentStatus = 'success' | 'unsupported_page' | 'failure';
|
|
99
|
+
type DocumentType = 'passport' | 'pan' | 'aadhaar' | 'driving_licence' | 'voter_id' | 'nrega_job_card' | 'npr_letter' | 'us_driver_license' | 'us_state_id' | 'passport_card' | 'us_green_card' | 'us_ead' | 'visa' | 'us_i94' | 'us_w9' | 'unknown';
|
|
100
|
+
type PageType = DocumentType | 'passport_biodata' | 'passport_non_biodata';
|
|
101
|
+
type UnsupportedReason = 'UNSUPPORTED_DOCUMENT';
|
|
102
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
103
|
+
[key: string]: JsonValue;
|
|
104
|
+
};
|
|
105
|
+
interface FieldEvidence {
|
|
106
|
+
text: string;
|
|
107
|
+
bbox: number[][];
|
|
108
|
+
source: 'ocr' | 'pdf_text' | 'mrz' | 'pdf417' | 'qr';
|
|
109
|
+
confidence: number;
|
|
110
|
+
page?: number;
|
|
111
|
+
}
|
|
112
|
+
interface BaseDocumentScanResult {
|
|
113
|
+
schemaVersion?: number;
|
|
114
|
+
documentFields?: Record<string, JsonValue> | null;
|
|
115
|
+
issuingCountry?: string | null;
|
|
116
|
+
issuingRegion?: string | null;
|
|
117
|
+
fieldEvidence?: Record<string, FieldEvidence[]>;
|
|
118
|
+
imageSize?: [number, number] | null;
|
|
119
|
+
checks?: Record<string, boolean | string | null>;
|
|
120
|
+
status: DocumentStatus;
|
|
121
|
+
documentType: DocumentType;
|
|
122
|
+
pageType: PageType;
|
|
123
|
+
confidence: number;
|
|
124
|
+
lowConfidence: boolean;
|
|
125
|
+
mrzRaw: [string, string] | null;
|
|
126
|
+
mrzValid: boolean;
|
|
127
|
+
unsupportedReason: UnsupportedReason | null;
|
|
128
|
+
identifierValid: boolean | null;
|
|
129
|
+
missingRequiredFields: string[];
|
|
130
|
+
backPageFields: BackPageFields | null;
|
|
131
|
+
panFields: PanFields | null;
|
|
132
|
+
aadhaarFields: AadhaarFields | null;
|
|
133
|
+
drivingLicenceFields: DrivingLicenceFields | null;
|
|
134
|
+
voterIdFields: VoterIdFields | null;
|
|
135
|
+
nregaJobCardFields: NregaJobCardFields | null;
|
|
136
|
+
nprLetterFields: NprLetterFields | null;
|
|
137
|
+
probeText: string[];
|
|
138
|
+
errors: string[];
|
|
139
|
+
warnings: string[];
|
|
140
|
+
processingMs: number;
|
|
141
|
+
}
|
|
142
|
+
interface SuccessfulDocumentScanResult extends BaseDocumentScanResult {
|
|
143
|
+
status: 'success';
|
|
144
|
+
fields: PassportFields | null;
|
|
145
|
+
}
|
|
146
|
+
interface UnsupportedPageDocumentScanResult extends BaseDocumentScanResult {
|
|
147
|
+
status: 'unsupported_page';
|
|
148
|
+
fields: null;
|
|
149
|
+
unsupportedReason: UnsupportedReason;
|
|
150
|
+
}
|
|
151
|
+
interface FailedDocumentScanResult extends BaseDocumentScanResult {
|
|
152
|
+
status: 'failure';
|
|
153
|
+
fields: PassportFields | null;
|
|
154
|
+
}
|
|
155
|
+
type DocumentScanResult = SuccessfulDocumentScanResult | UnsupportedPageDocumentScanResult | FailedDocumentScanResult;
|
|
156
|
+
type PassportScanResult = DocumentScanResult;
|
|
157
|
+
type ImageInput = File | Blob | Buffer | ArrayBuffer | string;
|
|
158
|
+
type ClientMode = 'local' | 'http' | 'lambda';
|
|
159
|
+
interface DocumentOCROptions {
|
|
160
|
+
mode?: ClientMode;
|
|
161
|
+
endpoint?: string;
|
|
162
|
+
functionName?: string;
|
|
163
|
+
timeoutMs?: number;
|
|
164
|
+
retries?: number;
|
|
165
|
+
apiKey?: string;
|
|
166
|
+
authHeaders?: (url: string, signal?: AbortSignal) => HeadersInit | Promise<HeadersInit>;
|
|
167
|
+
}
|
|
168
|
+
type PassportOCROptions = DocumentOCROptions;
|
|
169
|
+
interface ScanOptions {
|
|
170
|
+
documentType?: string;
|
|
171
|
+
country?: string;
|
|
172
|
+
includeEvidence?: boolean;
|
|
173
|
+
}
|
|
174
|
+
interface BatchScanResult {
|
|
175
|
+
results: DocumentScanResult[];
|
|
176
|
+
status: 'success' | 'partial' | 'failure';
|
|
177
|
+
errors: string[];
|
|
178
|
+
}
|
|
179
|
+
interface DocumentScanGroupResult extends BatchScanResult {
|
|
180
|
+
documentFields: Record<string, JsonValue>;
|
|
181
|
+
conflicts: Array<{
|
|
182
|
+
field: string;
|
|
183
|
+
values: Array<{
|
|
184
|
+
page: number;
|
|
185
|
+
value: JsonValue;
|
|
186
|
+
}>;
|
|
187
|
+
}>;
|
|
188
|
+
}
|
|
189
|
+
interface JobOptions extends ScanOptions {
|
|
190
|
+
grouped?: boolean;
|
|
191
|
+
notify?: boolean;
|
|
192
|
+
}
|
|
193
|
+
interface OCRJob {
|
|
194
|
+
id: string;
|
|
195
|
+
status: 'queued' | 'running' | 'succeeded' | 'failed';
|
|
196
|
+
createdAt: number;
|
|
197
|
+
expiresAt: number;
|
|
198
|
+
attempts: number;
|
|
199
|
+
error: string | null;
|
|
200
|
+
result?: BatchScanResult | DocumentScanGroupResult;
|
|
201
|
+
}
|
|
202
|
+
interface S3ImageInput {
|
|
203
|
+
bucket: string;
|
|
204
|
+
key: string;
|
|
205
|
+
versionId?: string;
|
|
206
|
+
}
|
|
207
|
+
interface DocumentDefinition {
|
|
208
|
+
documentType: DocumentType;
|
|
209
|
+
label: string;
|
|
210
|
+
countries: string[];
|
|
211
|
+
fields: string[];
|
|
212
|
+
requiredFields: string[];
|
|
213
|
+
requiredFieldsByVariant?: Record<string, string[]>;
|
|
214
|
+
requirementAlternatives?: Record<string, string[][]>;
|
|
215
|
+
sources: string[];
|
|
216
|
+
experimental: boolean;
|
|
217
|
+
}
|
|
218
|
+
interface DocumentCatalog {
|
|
219
|
+
schemaVersion: number;
|
|
220
|
+
documents: DocumentDefinition[];
|
|
221
|
+
capabilities: {
|
|
222
|
+
barcode: boolean;
|
|
223
|
+
jobs: boolean;
|
|
224
|
+
review: boolean;
|
|
225
|
+
multipage: boolean;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
declare class DocumentOCR {
|
|
230
|
+
private mode;
|
|
231
|
+
private endpoint?;
|
|
232
|
+
private functionName?;
|
|
233
|
+
private timeoutMs;
|
|
234
|
+
private retries;
|
|
235
|
+
private apiKey?;
|
|
236
|
+
private authHeaders?;
|
|
237
|
+
constructor(options?: DocumentOCROptions);
|
|
238
|
+
scan(image: ImageInput, options?: ScanOptions): Promise<DocumentScanResult>;
|
|
239
|
+
scanBatch(images: ImageInput[], options?: ScanOptions): Promise<BatchScanResult>;
|
|
240
|
+
scanDocument(images: ImageInput[], options?: ScanOptions): Promise<DocumentScanGroupResult>;
|
|
241
|
+
private uploadMany;
|
|
242
|
+
documents(): Promise<DocumentCatalog>;
|
|
243
|
+
enqueueJob(images: ImageInput[], options?: JobOptions): Promise<OCRJob>;
|
|
244
|
+
job(id: string): Promise<OCRJob>;
|
|
245
|
+
deleteJob(id: string): Promise<{
|
|
246
|
+
deleted: true;
|
|
247
|
+
}>;
|
|
248
|
+
scanS3(image: S3ImageInput, options?: ScanOptions): Promise<DocumentScanResult>;
|
|
249
|
+
stop(): Promise<void>;
|
|
250
|
+
private requireHttp;
|
|
251
|
+
private retry;
|
|
252
|
+
private request;
|
|
253
|
+
private invokeLambda;
|
|
254
|
+
}
|
|
255
|
+
declare const PassportOCR: typeof DocumentOCR;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Manages a local Python FastAPI server for passport OCR.
|
|
259
|
+
*
|
|
260
|
+
* Uses a module-level singleton so multiple PassportOCR instances
|
|
261
|
+
* share a single server process.
|
|
262
|
+
*/
|
|
263
|
+
declare class LocalServer {
|
|
264
|
+
private process;
|
|
265
|
+
private ready;
|
|
266
|
+
private startupAbort;
|
|
267
|
+
private cleanupHandler;
|
|
268
|
+
/**
|
|
269
|
+
* Ensure the local Python server is running and return its endpoint URL.
|
|
270
|
+
*/
|
|
271
|
+
ensureRunning(): Promise<string>;
|
|
272
|
+
private _start;
|
|
273
|
+
private _waitForReady;
|
|
274
|
+
/**
|
|
275
|
+
* Stop the local Python server if running.
|
|
276
|
+
*/
|
|
277
|
+
stop(): Promise<void>;
|
|
278
|
+
}
|
|
279
|
+
declare function getLocalServer(): LocalServer;
|
|
280
|
+
|
|
281
|
+
/** Preserve transport status separately from the server's human-readable message. */
|
|
282
|
+
declare class HttpError extends Error {
|
|
283
|
+
readonly status: number;
|
|
284
|
+
constructor(status: number, message: string);
|
|
285
|
+
get retryable(): boolean;
|
|
286
|
+
}
|
|
287
|
+
/** Invalid client input cannot succeed by retrying the same request. */
|
|
288
|
+
declare class InputValidationError extends Error {
|
|
289
|
+
constructor(message: string);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export { DocumentOCR, HttpError, InputValidationError, LocalServer, PassportOCR, getLocalServer };
|
|
293
|
+
export type { AadhaarFields, BackPageFields, BatchScanResult, ClientMode, DocumentCatalog, DocumentDefinition, DocumentOCROptions, DocumentScanGroupResult, DocumentScanResult, DocumentStatus, DocumentType, DrivingLicenceFields, FieldEvidence, ImageInput, JobOptions, JsonValue, NprLetterFields, NregaJobCardFields, NregaMember, OCRJob, PageType, PanFields, PassportFields, PassportOCROptions, PassportScanResult, S3ImageInput, ScanOptions, UnsupportedReason, VoterIdFields };
|