@demystify/extract
v0.3.0
Published
Client SDK for dmstfy-extract: schema-first document extraction with generated Zod types
Readme
@demystify/extract
TypeScript client SDK for dmstfy-extract — schema-first document extraction with generated Zod types for the starter schema library (invoice, bank statement, resume, contract, email).
pnpm add @demystify/extract # zod ^3.24 is a peerRun the service
This package is a client — it needs a running dmstfy-extract service.
Prebuilt service images are published to GHCR
(ghcr.io/demystify-systems/dmstfy-extract-api and
ghcr.io/demystify-systems/dmstfy-extract-worker). The fastest start is the
demo profile: one API container — in-memory store, jobs dispatched inline in
the API process (no worker or database needed), mock gateway — fully offline,
no API keys:
# docker-compose.yml — minimal offline demo
services:
extract-api:
image: ghcr.io/demystify-systems/dmstfy-extract-api:0.2 # match your client's 0.x minor
ports:
- "8402:8402"
environment:
DMSTFY_PROFILE: demo # memory store => inline worker; mock gateway => no keysthen point the client at http://localhost:8402. For the durable
Postgres-backed stack, add postgres and the worker image
(ghcr.io/demystify-systems/dmstfy-extract-worker, WORKER_DRIVER=pg,
DATABASE_URL=…, DMSTFY_EXTRACT_GATEWAY_DRIVER=mock|http) — the full compose
reference lives in the module's docker-compose.yml. Image/tag reference:
service images
· module README.
What you get
- Generated Zod schemas for each library schema version (
InvoiceV1, …) that validate the API result shape, including the{ raw, value }normalization envelope. - Envelope accessors —
fieldValue/fieldRaw/isNormalizedField— for the normalization envelopes returned on annotated fields. - Codegen (
@demystify/extract/codegen) to generate Zod modules from your own tenant schemas.
The normalization envelope
Fields whose schema carries x-demystify-normalize: date|amount|name come back
as { raw, value }. raw is the text exactly as printed; value is canonical
(ISO 8601 date / integer minor units / cleaned name). Amount envelopes add
currency (ISO 4217 or null) and exponent (minor-unit scale — INR/USD 2,
JPY 0, BHD 3), so ¥1234 → { raw: "¥1234", value: 1234, currency: "JPY", exponent: 0 }.
import { InvoiceV1 } from "@demystify/extract/generated";
import { fieldValue } from "@demystify/extract";
const invoice = InvoiceV1.parse(result.data);
fieldValue(invoice.total_amount); // integer minor unitsResults carry a top-level result_format: "normalized@1" version tag.
JSON-Schema → Zod codegen (bonus toolkit)
The codegen that produces this package's own generated/ modules is exported —
use it to generate checked-in Zod validators for your tenant schemas (the
gateway strict-output JSON-Schema subset, draft 2020-12: object/array/scalars,
enum/const, required, additionalProperties:false, anyOf/oneOf,
union type arrays, advisory format, and the x-demystify-normalize
envelope annotation). Deterministic output — commit the result and diff it in CI.
| Export (@demystify/extract/codegen, also re-exported from the root) | What it does |
| --- | --- |
| jsonSchemaToZodModule(schema, ref) → CodegenResult | whole generated module: { typeName, source } |
| zodExpr(schema) | one schema node → a Zod expression string |
| schemaRefToTypeName(ref) | "purchase_order@1" → "PurchaseOrderV1" |
| type CodegenResult, type JsonDict | result / input types |
(The runtime envelope accessors fieldValue / fieldRaw / isNormalizedField
pair with the generated code at parse time.)
Worked example — tiny schema in, generated module out:
import { jsonSchemaToZodModule } from "@demystify/extract/codegen";
const { typeName, source } = jsonSchemaToZodModule(
{
title: "Purchase order",
type: "object",
additionalProperties: false,
required: ["po_number", "total"],
properties: {
po_number: { type: "string" },
issued_on: { type: "string", "x-demystify-normalize": "date" },
total: { type: "string", "x-demystify-normalize": "amount" },
status: { enum: ["open", "fulfilled", "cancelled"] },
},
},
"purchase_order@1",
);
// typeName === "PurchaseOrderV1"; write `source` to src/generated/purchase_order_v1.tssource is exactly this module (verified against the built package):
// GENERATED by @demystify/extract codegen from schemas/library/[email protected] (Purchase order)
// Do not edit by hand — regenerate with `pnpm generate`.
import { z } from "zod";
export const PurchaseOrderV1 = z.object({
po_number: z.string(),
issued_on: z.object({ raw: z.string(), value: z.string().nullable() }).strict().optional(),
total: z.object({ raw: z.string(), value: z.number().int().nullable(), currency: z.string().nullable().optional(), exponent: z.number().int().optional() }).strict(),
status: z.enum(["open", "fulfilled", "cancelled"]).optional(),
}).strict();
export type PurchaseOrderV1 = z.infer<typeof PurchaseOrderV1>;Note the normalize annotations became { raw, value } envelope validators (with
currency/exponent on the amount), and additionalProperties: false became
.strict(). A split of this toolkit into a standalone @demystify/schema-tools
package is a 0.4 consideration — the import path above will keep working either
way within 0.x.
Versioning & compatibility
Pre-1.0 semver: a 0.x minor may contain breaking changes, a patch never does —
pin ~0.x.y. The client requires an extract service on the same 0.x minor;
servers reject unknown request fields with invalid_request, and the deprecated
x-tenant-id header alias is accepted through 0.3.x with removal targeted at
0.4.0. Full policy:
versioning-policy.md.
Changes: CHANGELOG.md.
See the module README for the full API, offline quickstart, and architecture.
License
MIT
