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

@maschinenlesbar.org/fim-portal-cli

v0.0.6

Published

TypeScript API client and CLI for the FIM Portal REST API (fimportal.de)

Readme

fim-portal-cli

A TypeScript API client and command-line interface for the open endpoints of the FIM Portal API (fimportal.de) — the central catalogue for XDatenfelder (Datenschemata, Datenfeldgruppen, Datenfelder, Dokumentsteckbriefe), XZuFi (Leistungen) and XProzess (Prozesse) FIM building blocks.

  • Zero runtime HTTP dependencies — built on Node's built-in http/https (no axios, no fetch polyfill).
  • One small dependency for the CLI: commander.
  • Strongly typed — every search filter and enum from the OpenAPI spec is a TypeScript type.
  • Well tested — unit tests on Node's built-in test runner (node --test), every HTTP response mocked.
  • Read-only scope — only the endpoints that need no authentication are implemented. Uploads, converters, quality-check tools and token introspection (all Access-Token-protected) are intentionally omitted.

New to FIM, XDatenfelder/XZuFi/XProzess, or terms like Nummernkreis and Freigabestatus? See GLOSSARY.md for the domain concepts and the project's own vocabulary.

Requirements

  • Node.js >= 20 (uses the stable built-in test runner, ESM and top-level await).

Install

npm install
npm run build        # compiles TypeScript to dist/

Run the CLI without a global install:

node dist/src/cli/index.js --help
# or, after `npm link` / global install:
fim-portal --help

CLI usage

The CLI mirrors the API resources. Every search command prints pretty JSON to stdout; download commands stream raw bytes (XML/PDF/CSV) to stdout or to a file via -o/--output.

Global options

| Option | Description | | --- | --- | | --base-url <url> | API base URL (default https://fimportal.de; https://schema.fim.fitko.net also works) | | --timeout <ms> | Per-request timeout (default 30000) | | --user-agent <ua> | User-Agent header value | | --max-retries <n> | Retries for transient 429/503 responses (default 2) | | --max-response-bytes <n> | Cap response body size in bytes (0 = unlimited; default 100 MiB) | | --compact | Print JSON on a single line | | -o, --output <file> | For downloads: write bytes to a file instead of stdout |

Global options go before the command, e.g. fim-portal --compact schemas get S07000009.

The -o/--output path is trusted input — it is written verbatim with no traversal or overwrite guard (you own your shell). If the write fails (for example the parent directory does not exist, or the path is not writable) the CLI reports a clean Error: could not write <path>: ... and exits 1 rather than a generic "Unexpected error".

Commands

schemas            search | versions | get | xdf | quality-report
document-profiles  search | versions | get | xdf
fields             search | versions | get | xdf
groups             search | versions | get | xdf
service-profiles   search | get | xzufi | pdf            (Leistungsteckbriefe)
service-texts      search | get | xzufi | pdf | parsed-xzufi   (Leistungsstammtexte)
organizational-units  list | xzufi
specializations       list | xzufi
online-services       list | xzufi
process-classes    search | get | xprozess
processes          search | get | xprozess | report | visualization | visualization-display
code-lists
search-csv         (tools/search-csv-download)

Examples

The identifiers below (e.g. L100001, 99050048262000) are illustrative placeholders, not guaranteed-live records — substitute IDs from a real search result. Note also that PDF exports expect a concrete language code as served by the API (not the generic de).

# Full-text search schemas, latest versions only, as compact JSON
fim-portal --compact schemas search --fts-query "Geburt" --is-latest --limit 5

# Filter fields by Feldart and Datentyp, two Freigabestatus values
fim-portal fields search --feldart input --datentyp text \
  --freigabe-status 5 --freigabe-status 6

# Fetch a full schema (version defaults to "latest")
fim-portal schemas get S07000009
fim-portal schemas get S07000009 1.0

# Download the XDatenfelder XML to a file
fim-portal -o geburt.xml schemas xdf S07000009 1.0

# Quality report
fim-portal schemas quality-report S07000009 latest

# Leistungen (XZuFi)
fim-portal service-profiles search --fts-query "Personalausweis" --sprache Deutsch
fim-portal service-texts get L100001 L1 leika
fim-portal -o service.pdf service-profiles pdf 99008001012012 de-DE

# XZuFi entities (cursor paginated)
fim-portal organizational-units list --limit 50
fim-portal organizational-units list --cursor 50

# Prozesse (XProzess)
fim-portal processes search --detaillierungsstufe 101 --is-musterprozess
fim-portal -o vis.pdf processes visualization PROC1 1.0 101

# Code lists & CSV export
fim-portal code-lists --limit 20
fim-portal -o fields.csv search-csv --resource fields --term Name

Note on search-csv: this command is a deliberate unvalidated pass-through to tools/search-csv-download. It exposes a convenient subset of the CSV filters and forwards their values verbatim — the OpenAPI spec types every CSV parameter as a free-form string (no enum), so unlike the validated fields search / schemas search paths there are no choices() guards here. The server validates the values.

Exit codes: 0 success, 4 on a 404 from the API, 1 for any other error, non-zero for usage errors.


Library usage

import { FimPortalClient, FimApiError } from "@maschinenlesbar.org/fim-portal-cli";

const client = new FimPortalClient(); // defaults to https://fimportal.de

// Typed search with auto-serialised filters
const page = await client.schemas.search({
  fts_query: "Geburt",
  freigabe_status: [5, 6],   // -> ?freigabe_status=5&freigabe_status=6
  is_latest: true,
  limit: 10,
});
console.log(page.total_count, page.items[0]?.name);

// Single resource ("latest" is the default version)
const full = await client.schemas.get("S07000009");

// Raw downloads return the bytes + content-type
const xdf = await client.schemas.downloadXdf("S07000009", "1.0");
await import("node:fs/promises").then((fs) => fs.writeFile("schema.xml", xdf.data));

try {
  await client.fields.get("ns", "DOES-NOT-EXIST");
} catch (err) {
  if (err instanceof FimApiError) console.error(err.status, err.detail);
}

Client options

new FimPortalClient({
  baseUrl: "https://schema.fim.fitko.net",
  timeoutMs: 15_000,
  maxRetries: 3,             // 429 / 503 are retried with linear backoff
  maxResponseBytes: 50 << 20, // abort responses larger than 50 MiB (0 = unlimited)
  userAgent: "my-app/1.0",
  transport: customTransport, // inject your own HTTP transport (see below)
});

Resource groups

client.schemas, .documentProfiles, .fields, .groups, .serviceProfiles, .serviceTexts, .organizationalUnits, .specializations, .onlineServices, .processClasses, .processes, .codeLists, .tools.


Architecture

src/
  client/
    enums.ts     # union types + runtime value arrays generated from the OpenAPI enums
    types.ts     # response interfaces (typed *Out summaries; full payloads as JsonObject)
    params.ts    # typed search-parameter objects per endpoint
    query.ts     # dependency-free query-string builder (repeated keys for arrays)
    http.ts      # the Transport interface + default node:http/https transport
    engine.ts    # URL building, retry/backoff, JSON/raw decoding, error mapping
    errors.ts    # FimError / FimApiError / FimNetworkError / FimParseError
    client.ts    # FimPortalClient — resource groups over the engine
  cli/
    io.ts        # injectable I/O seam (stdout/stderr/file)
    shared.ts    # option parsers, global-option resolver, JSON/raw renderers
    commands/    # one module per resource group
    program.ts   # assembles the commander program from injectable deps
    run.ts       # parses argv -> exit code (no process.exit; testable)
    index.ts     # #! bin shim

Design notes

  • The HTTP layer is a single Transport function ((req) => Promise<HttpResponse>). The default uses node:http/node:https; tests inject a mock. This keeps the client free of any HTTP framework.
  • The CLI is built around injectable CliDeps (client factory + I/O), so the whole program can be driven in-process by tests with a mocked client and captured output — no subprocesses.
  • Full single-resource payloads (e.g. FullSchemaOut, process trees) are deeply nested and standard-specific, so they are returned as faithful raw JsonObjects rather than partially-guessed types.

Testing

npm test          # builds, then runs `node --test` over dist/test
  • query.test.ts — query-string serialisation (arrays, dates, booleans, encoding).
  • http.test.ts — the default transport against a real loopback http.createServer.
  • engine.test.ts — URL building, JSON/raw decoding, error mapping, 429/503 retry — mocked transport.
  • client.test.ts — every endpoint's method/URL mapping + query serialisation — mocked transport.
  • cli.test.ts — end-to-end command parsing, rendering, file output and exit codes — mocked client.

All HTTP is mocked with Node's built-in node:test mock facility (test/helpers.ts); only http.test.ts touches a socket, and only on localhost.

Continuous integration

GitHub Actions workflows under .github/workflows/:

  • ci.yml — type-check, build and test on Node 20/22/24 for every push and PR.
  • release.yml — on a v* tag: verify the tag matches package.json, test, npm pack, and create a GitHub Release with the tarball.
  • publish.yml — manual dispatch: publish to npm via OIDC Trusted Publishing (no stored NPM_TOKEN) with provenance.
  • docs.yml — build TypeDoc API docs and deploy to GitHub Pages on each v* tag.

License

Dual-licensed — use it under either:

  • AGPL-3.0-or-later (default, free). Note the AGPL's §13 network clause: if you run a modified version as a network service, you must offer that modified source to the service's users.
  • Commercial license (paid), for closed-source / proprietary or SaaS use without the AGPL's obligations.

See LICENSING.md for details, and CONTRIBUTING.md for the contribution policy (this project does not accept external code contributions). Commercial enquiries: [email protected].