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

@soft9tech/s9terpsync

v0.2.0

Published

Standalone Slate CRM to Ellucian ERP synchronization through Ethos APIs; v1 supports Banner and future releases will add Colleague

Readme

S9TerpSync

S9TerpSync is a standalone, file-state Slate CRM to Ellucian ERP sync service for schools that need a small RHEL/Linux-friendly Node.js runtime without Docker, a database, or an external ETL platform. Version 1 supports Slate to Banner through Ethos APIs; Slate to Colleague is planned future support.

This project isn't a replacement for commercial ETL platforms. It's a small contribution to higher ed institutions that don't already have an ETL tool, or can't justify the cost of one, to help them sync data from Slate to Ellucian Banner or Colleague through Ethos APIs.

We're building the same sync process for other ETL platforms — Boomi, MuleSoft, and Jitterbit — in the coming weeks, and will publish those here as open-source pipelines too.

If your institution already has access to Ellucian Ethos Data Connect, we'd strongly recommend using that instead of any of these.

Requirements

  • Node.js >=24 <25
  • RHEL/systemd-friendly Linux
  • No Docker, no database

Install

Install from npm:

npm install -g @soft9tech/s9terpsync

Or build and run from source:

git clone https://github.com/soft9tech/s9terpsync.git
cd s9terpsync
npm ci
npm run build

Quick start

s9terpsync config init --source both --institution-id <school-id> --environment production --out /etc/s9terpsync/s9terpsync.yaml
s9terpsync config validate --file /etc/s9terpsync/s9terpsync.yaml
s9terpsync run --file /etc/s9terpsync/s9terpsync.yaml --source sftp --dry-run

If you built from source, run the same commands via node dist/cli.js in place of s9terpsync.

This quick start does not authorize scheduled LIVE production execution — dry-run review and explicit live-target configuration are prerequisites to that separate decision.

How data moves from Slate to Banner

A Banner person record is always matched or created first; every other entity depends on it (and admission decision, student attribute, and student cohort also depend on the admission application existing first). This is the real dependency order the stage engine enforces — src/stages/stage-registry.ts is the single source of truth for it.

flowchart TB
  slate["Slate CRM export<br/>SFTP file or API"]
  canonical["Decode, crosswalk,<br/>match to a Banner person"]

  subgraph entities["Nine target entities, in dependency order"]
    person["Person"]
    permanentAddress["Permanent address"]
    priorSchool["Prior school"]
    familyAddress["Family address"]
    familyRelationship["Family relationship"]
    admissionApplication["Admission application"]
    admissionDecision["Admission decision"]
    studentAttribute["Student attribute"]
    studentCohort["Student cohort"]

    person --> permanentAddress
    person --> priorSchool
    person --> familyAddress
    person --> familyRelationship
    person --> admissionApplication
    person --> admissionDecision
    admissionApplication --> admissionDecision
    person --> studentAttribute
    admissionApplication --> studentAttribute
    person --> studentCohort
    admissionApplication --> studentCohort
  end

  ethos["Ellucian Ethos APIs"]
  banner["Banner ERP"]

  slate --> canonical --> person
  entities --> ethos --> banner

  classDef pipeline fill:#EEEDFE,stroke:#534AB7,color:#26215C
  classDef mutation fill:#FAECE7,stroke:#993C1D,color:#4A1B0C
  classDef failure fill:#FAEEDA,stroke:#854F0B,color:#412402
  classDef readonly fill:#E1F5EE,stroke:#0F6E56,color:#04342C
  classDef neutral fill:#F1EFE8,stroke:#5F5E5A,color:#2C2C2A
  class canonical pipeline
  class person,permanentAddress,priorSchool,familyAddress,familyRelationship,admissionApplication,admissionDecision,studentAttribute,studentCohort,ethos,banner mutation
  class slate neutral

Core entities:

| Entity | What it carries | Ethos resource | | --------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------- | | Person | Match an existing Banner person (by Banner ID or Slate ID) or create one | persons v12 | | Permanent address | The applicant's Banner address | persons v12 | | Prior school | High school code, graduation date, GPA | high-school-information-high-school-details v1 | | Family address | Parent/guardian address | general-person-identification-address v1 | | Family relationship | Parent/guardian record and relationship type | guardian-information v1 | | Admission application | Application header, curricula, term/level/campus/program | admissions-application-curricula v1 | | Admission decision | Admit / deny / waitlist code and date | admissions-decision-processing v1 | | Student attribute | Attribute records tied to the application | admissions-application-contacts-cohorts-attributes v1 | | Student cohort | Cohort records tied to the application | admissions-application-contacts-cohorts-attributes v1 |

Full detail on each of these — including exactly what to grant on the Ethos/Banner side — is in the Ethos & Banner Prerequisites wiki page.

Logging

Both the CLI and the service emit structured logs as JSON — one object per line — on stdout. Nothing is written to a log file. Under the packaged systemd unit that stream lands in journald, alongside the plain-text human-readable output the process also writes (startup diagnostics, uncaught stack traces), so read it back with a filter that skips the non-JSON lines rather than a bare jq: journalctl -u s9terpsync -o cat | jq -R 'fromjson? // empty'.

Verbosity is controlled by the LOG_LEVEL environment variable. There's no s9terpsync.yaml field and no CLI flag for it — the environment variable is the only control.

  • Valid values are fatal, error, warn, info, debug, trace, and silent, matched case-insensitively (DEBUG and debug are the same value).
  • Unset or empty means info.
  • Anything else is rejected rather than ignored. The CLI writes a fixed diagnostic naming the seven valid levels — it never echoes the value you supplied — and exits 1. The service fails to start: it throws during startup, before it binds its HTTP port, so under the unit's Restart=on-failure it restart-loops until the value is corrected.

The level is read once, at startup. Changing it means restarting the process; a running service won't pick up a new value on its own. Setting LOG_LEVEL on the systemd unit is covered in Service & Systemd Operation.

Documentation

Full setup, configuration, and operations guidance lives in the project wiki:

License

Licensed under Apache-2.0.