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

@beparallel/grouping

v1.0.3

Published

Node.js wrapper for the official ATIH grouping engine (PMSI GHM/GHS) used in French hospital medical coding. This package provides access to the native C groupage module for classifying inpatient stays into GHM and GHS codes as part of the PMSI/T2A system

Downloads

1,198

Readme

grouping

A Node.js wrapper for the official ATIH grouping engine used in the French PMSI (Programme de Médicalisation des Systèmes d'Information).

This package allows integration of the native C-based groupage module to classify hospital stays into GHM (Groupe Homogène de Malades) and GHS (Groupe Homogène de Séjours) codes, in compliance with the T2A billing system in France.

Overview

This project implements a Node.js native addon using Node-API to wrap the official ATIH (Agence Technique de l'Information sur l'Hospitalisation) grouping engine. The grouping engine is a critical component of the French healthcare system's billing and classification infrastructure.

French Medical Coding Context

The French healthcare system uses the PMSI (Programme de Médicalisation des Systèmes d'Information) for medical activity coding and hospital billing under the T2A (Tarification À l'Activité) system. This project specifically addresses the need for automated grouping of medical stays into standardized categories.

For comprehensive background on grouping algorithms and medical coding in France, refer to this detailed overview.

Technical Architecture

This package consists of:

  1. Native C/C++ Module: The core ATIH grouping engine in the atih/ folder (see doc/atih/grouping_method.md)
  2. JavaScript Interface: High-level Node.js API in src/ that converts JS objects to ATIH RSS strings and parses the native result
  3. Type Definitions: TypeScript definitions for development support
  4. Runtime data: ATIH reference tables shipped with the package under tables/V2024, tables/V2025, tables/V2026

Repository layout

| Path | Purpose | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | atih/ | Native C sources for the ATIH grouping engine (grouping/) and the FICUM control module (ficum/) | | src/ | TypeScript API: exports/ (public functions + types), utils/ (RUM model, 022/023 format serializers, enums) | | tables/V{2024,2025,2026}/ | ATIH reference tables loaded at runtime by year | | lib/, txt/ | Supporting ATIH data files | | doc/ | ATIH format specs and the publish runbook | | tools/grouping-fixtures/ | Tooling to capture deterministic test fixtures from the AlphaCode API (see its README) | | src/tests/fixtures/ | Captured .case.json fixtures asserted by the grouping test suite |

The native engine loads its tables relative to the package root (resolved via require.resolve('@beparallel/grouping/package.json')), not the consuming app's working directory. The tables/, lib/, and txt/ folders are part of the published tarball (files in package.json).

Public API

The package entry point exports the grouping functions, the error model, and all types:

import {
  grp, // auto-selects the engine by discharge year
  grp2024,
  grp2025,
  grp2026,
  GroupingError,
  BlockingErrorGroup,
  RUMEnums // TYPE_ETAB, RUM_ENTRY_MODE, RUM_EXIT_MODE, ...
} from '@beparallel/grouping'

The RUMObject and TYPE_ETAB types used to build input are also available from the generated types path (this is how the AlphaCode valuation service imports them):

import type { RUMObject } from '@beparallel/grouping/dist/types/utils'

Year selection

grp() inspects the discharge dates on the rumObjects and routes to the matching engine:

  • 2026 staysgrp2026 (serializes RUMs to the 023 RSS format)
  • 2025 staysgrp2025 (serializes to the 022 format)
  • 2024 staysgrp2024 (serializes to the 022 format)
  • No year detected → falls back to grp2026 (with a console warning)

Call grp2024 / grp2025 / grp2026 directly when you want to force a specific year.

Input

All grouping functions take a single object validated by Zod:

interface GroupingInput {
  rumObjects: RUMObject[] // one or more Résumés d'Unité Médicale
  typeEtab: TYPE_ETAB // 1 = PUBLIC_HOSPITAL (ex-DGF), 2 = PRIVATE_HOSPITAL (ex-OQN)
}

A RUMObject mirrors an ATIH RSS line. Dates are DDMMYYYY strings; diagnoses are CIM-10 codes; ZAs are CCAM act rows. A real (captured) example:

const input = {
  typeEtab: 1, // PUBLIC_HOSPITAL
  rumObjects: [
    {
      FINESS: '511928447',
      RSS: '22676886701954659349',
      RUM: '7319798297',
      birthDate: '22011959',
      sex: '1',
      uMed: 'RAC',
      entryDate: '19122025',
      entryMode: '8',
      exitDate: '22122025',
      exitMode: '8',
      nDA: 2,
      nZA: 5,
      DP: 'G553', // principal diagnosis
      DAs: ['M4806', 'E6694'], // associated diagnoses
      ZAs: [
        // CCAM acts
        { date: '19122025', code: 'LFFA001', phase: '0', act: '1', mod: 'JK', AN: '1', qty: '1' },
        { date: '19122025', code: 'LFAA002', phase: '0', act: '4', diag: '1', AN: '2', qty: '1' }
      ]
    }
  ]
}

const result = grp(input) // → grp2025 (2025 discharge)

The full field set lives in RUMObjectSchema (src/utils/types/rum.types.ts); the enums (entry/exit modes, sex, RAAC, etc.) are in src/utils/types/rum.enums.ts and re-exported as RUMEnums. For more complete examples, browse the captured cases under src/tests/fixtures/.

Output

Each function returns a year-specific result (Grp2024Result / Grp2025Result / Grp2026Result, see src/exports/types/) shaped like:

interface Grp2026Result {
  returnCode: number // raw ATIH return code
  returnError: GroupingError | null // mapped error (null when grouping succeeded)
  grp_res: {
    // classification result
    cmd: string // Catégorie Majeure de Diagnostic
    ghm: string // Groupe Homogène de Malades
    version: string
    cret: string
  }
  ghs_val: { ghs: string /* + supplement/billing counters */ }
  rss_inf: { agea: number; dstot: number /* + diag/act lists, signatures */ }
}

Error model

When the engine cannot group a stay it returns a non-zero returnCode, which is mapped to a GroupingError via the dictionary in src/exports/error-management/grouping-errors.dict.ts:

interface GroupingError {
  code: number
  errorName: string
  scope: string | null
  blockingErrorGroup: BlockingErrorGroup | null // 90Z00Z, 90Z01Z, 90Z02Z, 90Z03Z
  description: string
}

BlockingErrorGroup distinguishes mandatory-control errors from group-1/group-2 blocking errors and FG implementation errors. On success, returnError is null and returnCode is 0.

How It's Built

The build pipeline has two stages: native C compilation and TypeScript compilation. Prebuilt binaries are distributed via GitHub Releases so consumers don't need a local C toolchain.

Native C Compilation

node-gyp compiles the ATIH C source files defined in binding.gyp into .node binary addons. Four targets are built:

  • grp2026 -- the 2026 grouping engine (FG distribution with 2025+2026 stays; fgmco_2026)
  • grp2025 -- 2025 stays via the same FG stack as 2026 (fgmco_2026 + SRCV2025/2026)
  • grp2024 -- 2024 stays (fgmco_2025 + SRCV2024/2025)
  • ficum -- FICUM control module (UM authorization file generation/validation)

Build locally with:

pnpm build:c:native

TypeScript Compilation

tsc compiles the src/ directory into CommonJS output in dist/, with type declarations emitted to dist/types/.

pnpm build:ts

Local Development

# Use the pinned Node version (.nvmrc → 24.14.0)
nvm use 24

pnpm install

# Install prebuilt native binaries. `prebuild:install` resolves them in this order:
#   1. If GITHUB_TOKEN is set -> download from the GitHub Release.
#   2. Otherwise -> extract a matching tarball committed under prebuilds/.
#   3. If neither works -> prints guidance to build from source.
pnpm prebuild:install

# Optional: only needed for the remote download path
# cp .env-example .env.dev   # set GITHUB_TOKEN
# export GITHUB_TOKEN=...    # token with access to the GitHub Releases

# ...or compile the native modules from source (needs python3, make, g++)
pnpm build:c:native

pnpm test

Running Tests

Tests require the native C modules to be compiled first. The pretest script handles this automatically:

pnpm test

This runs pnpm build:c:native (via the pretest hook) then jest. To skip the native rebuild when the C code hasn't changed:

npx jest

To run only the grouping fixture suite:

pnpm test:fixtures

To run a specific test file:

npx jest src/tests/grouping.test.ts
npx jest src/tests/ficum.test.ts

To update snapshots after an intentional behavior change:

npx jest --updateSnapshot

Test fixtures

The grouping suite is driven by captured .case.json fixtures under src/tests/fixtures/**, auto-discovered by src/tests/fixtures/loader.ts and asserted by src/tests/grouping.test.ts.

Fixtures are generated from the AlphaCode compare-to-reference-grouping admin API response using pnpm capture-fixtures. See tools/grouping-fixtures/README.md for the full capture workflow, the Metabase baseline query, and where matches vs. mismatches land.

Prebuilt Binaries

To avoid requiring consumers to compile C code locally, the project uses prebuild and prebuild-install plus a small wrapper that adds a local-tarball fallback:

  • Contents: Each tarball includes all native targets from binding.gypbuild/Release/grp2024.node, grp2025.node, grp2026.node, and ficum.node—under the same paths as a local node-gyp build. The binary.module_name field in package.json (grp2026) is the asset name prebuild-install resolves for this package; it does not mean only that single file is shipped.
  • Publishing: On a version tag, .github/workflows/prebuild.yml creates a GitHub Release and runs prebuild on ubuntu-latest and macos-latest (Node ABI aligned with CI), uploading one tarball per OS/arch matrix row. The same workflow also supports workflow_dispatch (manual run), in which case it builds the same matrix but uploads to workflow artifacts instead of a release—so maintainers can pull them locally with pnpm prebuild:download and commit them under prebuilds/.
  • Installing: Consumers run pnpm prebuild:install (see scripts/install-prebuild.cjs). The wrapper resolves the binary in this order:
    1. If GITHUB_TOKEN is set, delegates to prebuild-install -T $GITHUB_TOKEN (download from the GitHub Release host).
    2. Otherwise (or on failure), detects libc via detect-libc and walks an ordered list of candidate tarballs under prebuilds/<scope>/:
      • glibc Linux: linuxglibc-{arch} then linux-{arch}
      • macOS / other: {platform}-{arch}
    3. The first existing candidate is extracted into the package root, populating build/Release/. If none exist, the wrapper exits with guidance pointing to pnpm prebuild:create (build a local tarball) or pnpm build:c:native (compile from source). Use pnpm prebuild:install:remote to bypass the wrapper and run the original prebuild-install -T $GITHUB_TOKEN directly.
  • Committed tarballs: The matrix produced by a manual prebuild.yml run can be committed under prebuilds/ and is shipped via the files array in package.json. End consumers on a matching node_abi + platform + arch therefore do not need a token to install. The pinned Node ABI for the committed set is the value of .nvmrc at release time (currently 24.14.0).
  • Postinstall: postinstall runs pnpm build, which compiles TypeScript only. Downloading prebuilds is not automatic; run prebuild:install after install, or compile everything from source with pnpm build:c:native in the package directory.

CI Pipeline

| Workflow | Trigger | Purpose | | ------------------------------ | --------------------------- | ---------------------------------------------------------------------------------------- | | prebuild.yml | Push tag v* / manual | Tag push: creates a GitHub Release and uploads prebuilts (macos x64/arm64, linux glibc x64/arm64). Manual dispatch: builds the same matrix and uploads workflow artifacts (download with pnpm prebuild:download) | | test.yml | Pull request | Runs tests using the Node version from .nvmrc |

Package Manager

The project uses pnpm, pinned via corepack through the packageManager field in package.json.

Installation

  1. Install the package
pnpm add @beparallel/grouping
  1. (Optional) Export a GitHub Token

The package ships committed prebuilt tarballs under prebuilds/ for the supported node_abi + platform + arch combinations, so a token is not required when your runtime matches that set. Only set a token if you need to download a fresh build from the GitHub Release host (e.g. on a Node version or arch that isn't covered):

export GITHUB_TOKEN=your_github_token

or set it in the .env.dev file and run dotenv -f .env.dev run XXX.

  1. Install the native module
cd node_modules/@beparallel/grouping && pnpm prebuild:install

prebuild:install will try GITHUB_TOKEN first if set, and otherwise extract the committed local tarball. If neither path produces binaries it prints guidance for pnpm prebuild:create or pnpm build:c:native.

In the AlphaCode monorepo this step is wired into the valuation service as the grouping:build script.

Requirements

  • Node.js 24.0.0 or higher (the repo pins 24.14.0 in .nvmrc)
  • Compatible with macOS and Linux (Windows is not supported)
  • For native grouping: either run prebuild:install after adding the dependency (downloads compiled .node files), or install a C toolchain (python3, make, g++) and run pnpm build:c:native inside the package

📚 Additional Resources

Release process

ATIH

The non-grouped 022/023 RSS serialization is implemented in code under src/utils/rum/formats/ rather than a standalone doc.