npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aidex/document

v1.1.0

Published

Aidex Document — the first Aidex Feature Pack: engine identifiers and typed request/response contracts for document intelligence (extraction, OCR, translation, summarization, resume analysis, invoice extraction, contract review).

Readme

@aidex/document

Installation

pnpm add @aidex/document
npm install @aidex/document

The first official Aidex Feature Pack — and the reference architecture every future Feature Pack should copy.

What this is

A Feature Pack is a domain-specific capability layer built on the Aidex platform, consumed the same way any external developer would consume it — never granted special access to packages/core or any other platform package. @aidex/document covers document intelligence: extraction, OCR, translation, summarization, resume analysis, invoice extraction, and contract review.

Status

| Phase | What it added | State | | --- | --- | --- | | 1 | Engine ids + typed request/response contracts | Done | | 2 | Engine implementations for all 7 ids — validated input, NotImplementedError for the AI call | Done | | 3 | The first real, AI-powered engine: DocumentSummarizeEngine | Done — established the reference architecture | | 4 | Five more real engines, following that exact architecture: DocumentExtractEngine, DocumentTranslateEngine, ResumeAnalysisEngine, InvoiceExtractionEngine, ContractReviewEngine | Done | | Expansion Phase 1 | 4 new engine ids + typed request/response contracts: document.classify, document.keywords, document.transform, document.review | Done | | Expansion Phase 2 | Engine implementations for all 4 — validated input, deterministic placeholder results, no AI | Done | | Expansion Phase 3 | AI integration for all 4 — prompt → provider → structured result, following DocumentSummarizeEngine's exact architecture | Done | | Expansion Phase 4 | 4 reusable multi-engine workflows composing the AI-backed engines above | Done |

All 11 engines in this package are now AI-backed except document.ocr. document.classify/document.keywords/ document.transform/document.review were upgraded from their own Phase 2 deterministic placeholders to real Strategy-owning engines the same way the original 6 engines were — version: '1.0.0', own prompt, own Strategy, text-only for now (see below). They exist to close real capability gaps identified when scoping document workflows (document type routing, keyword extraction, generic reformatting, and a domain-neutral review distinct from contract.review). contract.review itself is unchanged — these are new, separate engines, not replacements. 4 workflows now compose these engines via @aidex/workflow's real Workflow/WorkflowStep/WorkflowExecutor contract: document.workflow.document-review, document.workflow.document-analysis, document.workflow.document-transformation, document.workflow.document-localization. No new engines, no new prompts, no new providers, no engine contract changes.

DocumentOcrEngine is the one executable engine deliberately left as a Phase 2 placeholder — it validates input and throws NotImplementedError. OCR is Vision AI's job, not Document AI's: it belongs to a future @aidex/vision Feature Pack, or to an OCR-capable Provider, neither of which exists yet. Every other engine in this package also only handles text/* sources for exactly this reason — see "Design decisions" below.

Dependencies

@aidex/core and @aidex/engines (the Engine/ExecutionContext contracts this package implements), @aidex/prompts (prompt registration/rendering), @aidex/observability (optional cost/token/duration tracking), and @aidex/workflow (for Workflow/WorkflowStep/WorkflowExecutor, added for Expansion Phase 4). @aidex/providers is a dev-only dependency, used solely by tests (StubProvider) — never imported by anything this package ships. No provider SDK is imported anywhere in this package, and no vendor is hardcoded.

Engine identifiers

import { DocumentEngineId } from '@aidex/document';

DocumentEngineId.Extract;       // 'document.extract'
DocumentEngineId.Ocr;           // 'document.ocr'  — still a placeholder
DocumentEngineId.Translate;     // 'document.translate'
DocumentEngineId.Summarize;     // 'document.summarize'
DocumentEngineId.Classify;      // 'document.classify'
DocumentEngineId.Keywords;      // 'document.keywords'
DocumentEngineId.Transform;     // 'document.transform'
DocumentEngineId.Review;        // 'document.review'
DocumentEngineId.ResumeAnalyze; // 'resume.analyze'
DocumentEngineId.InvoiceExtract;// 'invoice.extract'
DocumentEngineId.ContractReview;// 'contract.review'

Types

Every capability has a request and a result type, both built on the shared DocumentSource input:

interface DocumentSource {
  content: string;   // base64 for binary mime types, plain text for text/plain
  mimeType: string;
  filename?: string;
}

| Engine id | Request | Result | Strategy | Prompt id | | --------------------- | ---------------------------- | ---------------------------- | ----------------------------- | ----------------------- | | document.extract | DocumentExtractRequest | DocumentExtractResult | DocumentExtractStrategy | document.extract | | document.ocr | DocumentOcrRequest | DocumentOcrResult | (none — placeholder) | (none) | | document.translate | DocumentTranslateRequest | DocumentTranslateResult | DocumentTranslateStrategy | document.translate | | document.summarize | DocumentSummarizeRequest | DocumentSummarizeResult | DocumentSummarizeStrategy | document.summarize | | document.classify | DocumentClassifyRequest | DocumentClassifyResult | DocumentClassifyStrategy | document.classify | | document.keywords | DocumentKeywordsRequest | DocumentKeywordsResult | DocumentKeywordsStrategy | document.keywords | | document.transform | DocumentTransformRequest | DocumentTransformResult | DocumentTransformStrategy | document.transform | | document.review | DocumentReviewRequest | DocumentReviewResult | DocumentReviewStrategy | document.review | | resume.analyze | ResumeAnalysisRequest | ResumeAnalysisResult | ResumeAnalysisStrategy | resume.analyze | | invoice.extract | InvoiceExtractionRequest | InvoiceExtractionResult | InvoiceExtractionStrategy | invoice.extract | | contract.review | ContractReviewRequest | ContractReviewResult | ContractReviewStrategy | contract.review |

Example usage

DocumentSummarizeEngine — result is plain text, no JSON parsing needed:

import { DocumentSummarizeEngine } from '@aidex/document';
import { GeminiProvider } from '@aidex/providers';
// Any Provider works here — GeminiProvider is just today's example.
// Swapping it for a future OpenAI/Claude provider requires no change
// below: the engine only ever calls context.provider, never a vendor SDK.

const provider = new GeminiProvider({ apiKey: process.env.GEMINI_API_KEY });
const engine = new DocumentSummarizeEngine();

const result = await engine.execute({
  config: { provider },
  provider,
  request: {
    strategy: 'document.summarize',
    input: {
      source: { content: 'The full text of a long document...', mimeType: 'text/plain' },
      maxLength: 50,
    },
  },
});

console.log(result.summary);

InvoiceExtractionEngine — result is a structured, strongly-typed object; the strategy asks the provider for strict JSON and parses it:

import { InvoiceExtractionEngine } from '@aidex/document';

const engine = new InvoiceExtractionEngine();

const result = await engine.execute({
  config: { provider },
  provider,
  request: {
    strategy: 'invoice.extract',
    input: { source: { content: fullInvoiceText, mimeType: 'text/plain' } },
  },
});

console.log(result.totalAmount, result.currency, result.lineItems);

With cost/token tracking (optional, and never tied to a specific vendor's pricing) — every engine in this package accepts the same config shape:

import { DocumentSummarizeEngine } from '@aidex/document';
import { ObservabilityBus } from '@aidex/observability';

const observability = new ObservabilityBus();
observability.subscribe((event) => console.log(event.event, event.metadata));

const engine = new DocumentSummarizeEngine({
  observability,
  pricing: { inputPricePerMillion: 0.075, outputPricePerMillion: 0.3 }, // whatever the configured provider actually charges
});

Registered in an EngineRegistry alongside other engines, dispatched by id exactly like any other engine in the platform:

import { EngineRegistry } from '@aidex/engines';
import { DocumentSummarizeEngine, DocumentEngineId } from '@aidex/document';

const registry = new EngineRegistry();
registry.register(new DocumentSummarizeEngine());

const result = await registry.execute(DocumentEngineId.Summarize, {
  config: { provider },
  provider,
  request: { strategy: DocumentEngineId.Summarize, input: { source: { content: '...', mimeType: 'text/plain' } } },
});

Feature Package Manifest

DOCUMENT_FEATURE_PACKAGE (a FeaturePackage from @aidex/sdk) bundles every engine, prompt, and catalog-metadata entry this package ships, ready for AIBuilder.use(DOCUMENT_FEATURE_PACKAGE):

import { AIBuilder } from '@aidex/sdk';
import { DOCUMENT_FEATURE_PACKAGE } from '@aidex/document';

const ai = new AIBuilder().provider(myProvider).use(DOCUMENT_FEATURE_PACKAGE).build();

Every engine in this manifest is a singleton — constructed once, at module load, and shared across every EngineRegistry that registers it. Engine implementations must stay stateless: all execution state belongs on ExecutionContext, never on the engine instance itself.

workflows (DocumentReviewWorkflow, DocumentAnalysisWorkflow, DocumentTransformationWorkflow, DocumentLocalizationWorkflow) is pass-through only — AIBuilder.use() never registers, adapts, or executes them. Call each workflow's own .run(input, provider, options) directly.

Design decisions

Engine + Strategy, not Engine alone. Every real engine validates input and delegates to a dedicated Strategy, which does the actual work: renders the prompt, calls the provider, parses the response, and records observability. This mirrors @aidex/providers' own layering (GeminiProvider does the vendor call; a Strategy like TextGenerationStrategy sits above it) one level up — the Engine owns dispatch, the Strategy owns AI execution. All six real engines in this package follow the identical split established by DocumentSummarizeEngine.

No nested Aidex kernel inside any Engine. Every execute() calls this.strategy.execute(request, context) directly — the exact two-argument signature @aidex/core's own Aidex.execute() uses to dispatch a Strategy internally. Constructing a throwaway new Aidex({ provider: context.provider }) just to immediately call the one Strategy an Engine already holds a reference to would fire boot/ready lifecycle hooks nobody's listening to — ceremony with no behavior behind it. context already carries everything a kernel would supply here (the configured Provider).

Prompt registered privately, per engine instance. Each engine owns its own PromptRegistry, created in its constructor and populated with exactly the one prompt it needs. No shared/global registry, no dependency on @aidex/plugins' PluginManager — this package doesn't use plugins at all, per scope.

Observability lives in the Strategy, not the Engine — via one shared helper. The Strategy is the layer that actually calls context.provider.generate() and receives the raw ProviderResponse (including metadata.usage, if the provider supplied it) — the same reason GeminiProvider, not some layer above it, is where @aidex/providers records its own observability. Rather than every Strategy reimplementing the same timing/try-catch/track* block, all six call the shared callProviderWithObservability() (src/observability/), established once DocumentSummarizeStrategy proved the pattern and then extracted so five more strategies could reuse it verbatim instead of re-deriving it. Reading metadata.usage is duck-typed (unknown, checked at runtime), not imported from @aidex/providers — this package never depends on a specific provider's types.

JSON-backed results share a small parsing toolkit. DocumentExtractResult, DocumentTranslateResult, ResumeAnalysisResult, InvoiceExtractionResult, and ContractReviewResult are all structured objects, not plain strings — their strategies prompt for "strict JSON only" and parse the response. Three small, genuinely-shared pieces back every one of them (src/parsing/): parseJsonResponse() (strips a ```json fence if present, then JSON.parses, throwing UnparsableProviderResponseError on failure) and asString/asNumber/asStringArray/asRecord (defensive readers over unknown — never throw, so each strategy's own parse function decides which missing/malformed field is fatal vs. just omitted). A parse function that finds the required container shape missing (e.g. no "fields" key at all) throws; one that finds an individual optional field malformed (e.g. a non-numeric matchScore) just omits it — providers occasionally under-fill optional fields, and that's not the same failure as returning prose instead of JSON.

Text-only for now, everywhere. Every strategy rejects any DocumentSource whose mimeType doesn't start with text/. Processing a scanned PDF or image requires OCR first, and DocumentOcrEngine is the one engine in this package intentionally left unimplemented — OCR is Vision AI, not Document AI, and belongs to a future @aidex/vision Feature Pack or an OCR-capable Provider. Rather than silently mis-processing binary content or building a partial OCR pipeline out of scope for this pack, every strategy fails loudly and says why.

Pricing is caller-supplied, never hardcoded. DocumentEnginePricing mirrors @aidex/providers' GeminiPricing shape but names nothing vendor-specific — whoever configured the Provider also knows that provider's current rates and supplies them.

Expansion engines (classify/keywords/transform/review) all take a plain DocumentSource, never another engine's Result. This was a deliberate constraint, not an oversight: document.extract's Result{fields: Record<string,string>} doesn't fit any other engine's Request, and no new engine here was designed to accept it either — every engine in this pack stays independently callable with just a source, matching the convention every existing engine already established. A document workflow composing several of these will bundle independent calls on the same source rather than chain one engine's output into another's input, unless a future phase deliberately designs that composition.

document.review is a genuinely new, domain-neutral engine — not a generalized contract.review. Its DocumentReviewFinding{issue, severity, recommendation} mirrors ContractRisk's shape closely (same three-field structure), but uses domain-neutral naming (issue instead of clause) since it reviews any document, not specifically contracts. contract.review itself is untouched — the two are separate, parallel capabilities, not a refactor.

classification is a genuinely new catalog category — every other new tag reuses this pack's own existing categories. document.keywords uses extraction (matching document.extract/invoice.extract); document.transform uses transformation (matching @aidex/design's/ @aidex/media's usage); document.review uses analysis (matching contract.review/resume.analyze, both already in this same file). Only document.classify needed a category with no existing analog anywhere in the catalog.

Expansion Phase 2 engines followed DocumentOcrEngine's shape (no Config, no Strategy) only until Phase 3 upgraded them to DocumentSummarizeEngine's Config/Strategy-owning shape — the same lifecycle the original 6 engines went through in Phases 2→3/4. Phase 2 returned deterministic placeholders directly from execute(); Phase 3 moved all four to render a prompt, call the Provider, and parse a structured response, exactly like every other AI-backed engine here.

document.transform's required targetFormat is validated by its Strategy, not its Engine — the same division of responsibility DocumentTranslateEngine/DocumentTranslateStrategy already established for targetLanguage. assertHasValidSource (called at the Engine layer) only ever proves source; every request-specific required field beyond that is the Strategy's own responsibility to check, a precedent this package established before this expansion touched it. document.transform's mimeType stays deterministically resolved from targetFormat in the Strategy — never asked of the provider — while content is the one genuinely AI-generated field.

document.keywords' Phase 3 prompt asks the provider for the same thing its Phase 2 placeholder computed heuristically — key phrases from the document's actual content — rather than a different capability; the placeholder-to-AI upgrade preserves what the engine means, not just its Result shape.

metadata.test.ts's PLANNED_IDS list from Expansion Phase 1 was retired in Phase 2, once real Engine classes existed for all four — no further metadata test changes were needed for Phase 3, since bumping metadata.ts's versions to 1.0.0 and the real engines' own readonly version = '1.0.0' to match is exactly what the existing it.each(REAL_ENGINES) assertion was already designed to verify.

3 of 4 Expansion Phase 4 workflows are genuinely data-dependent pipelines; the 4th is an honestly-documented independent bundle — the same split @aidex/marketing's Phase 4 established. DocumentReviewWorkflow adapts document.extract's structured fields into a synthetic text/plain DocumentSource (via a local fieldsToDocumentSource helper) so document.review reviews what was actually extracted, not the raw original document. DocumentTransformationWorkflow and DocumentLocalizationWorkflow both wrap their first step's genuinely textual output (DocumentTransformResult.content/DocumentTranslateResult .translatedText) into a new DocumentSource so document.summarize summarizes the transformed/translated text, not the original. DocumentAnalysisWorkflow's two steps (document.classify, document.keywords) are not chained — document.classify's Result is a label (documentType), not document content, so there's no meaningful DocumentSource to adapt it into. Both steps read the original source directly; the workflow's value is bundling classification + keywords into one call with shared lifecycle/cancellation/error handling, not fabricating a dependency the engines don't support.

Every cross-step adaptation goes through DocumentSource and nothing else — no engine's Request/Result type was touched. This was an explicit constraint for this phase ("adapt it through DocumentSource inside the workflow only... Do not modify engine contracts"), and every workflow here honors it: the adaptation logic (fieldsToDocumentSource, wrapping content/translatedText into {content, mimeType}) lives entirely inside the workflow files, never inside an engine or Strategy.

DocumentTransformationWorkflow inherits document.summarize's text-only constraint. Since document.transform's mimeType is resolved from the caller's targetFormat and document.summarize's Strategy only accepts text/* sources, this workflow only produces a meaningful summary for text-format targets (markdown, plain-text, html) — documented on the workflow itself rather than silently failing in a confusing place.

Workflow class names stay Document-prefixed (DocumentReviewWorkflow, not ReviewWorkflow), matching this package's own established engine-naming convention (DocumentSummarizeEngine, etc.) — a deliberate difference from @aidex/media's/@aidex/marketing's unprefixed workflow names, which themselves matched those packages' own unprefixed engine conventions. Each Feature Pack's workflows follow its own established prefix, not a single cross-pack rule.

Why this is the reference architecture

  • Standalone by default, platform-integrated by necessity. Types and ids need nothing; every AI-backed engine needs @aidex/core, @aidex/engines, @aidex/prompts, and @aidex/observability — and only those, added exactly when the capability that needed them was built.
  • Consumes, never modifies, the platform. Nothing in this package changes @aidex/core, @aidex/sdk, @aidex/providers, @aidex/plugins, @aidex/workflow, @aidex/prompts, @aidex/tools, @aidex/observability, or @aidex/evaluation.
  • Provider-agnostic, proven, not just claimed. No strategy in this package imports a vendor SDK or branches on context.provider.name — every engine's test suite includes a "provider independence" test that runs the same engine against two differently-shaped inline providers and asserts both work unchanged.
  • One pattern, proven once, then reused five times. DocumentSummarizeEngine established the Engine+Strategy split, the direct strategy.execute(request, context) call, and the observability wrapper. DocumentExtractEngine, DocumentTranslateEngine, ResumeAnalysisEngine, InvoiceExtractionEngine, and ContractReviewEngine all reuse the identical shared infrastructure (callProviderWithObservability, parseJsonResponse, the coerce helpers) rather than each re-deriving it — proof the pattern generalizes, not just that it worked once.