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

@saptools/cf-sync

v0.4.9

Published

Sync SAP BTP Cloud Foundry topology and HANA DB bindings to local package-managed JSON files

Readme

☁️ @saptools/cf-sync

Map your SAP BTP Cloud Foundry topology and HANA app bindings into package-managed JSON files.

Walk every region, org, space, and app you have access to, cache the topology, then optionally collect app-level HANA credentials in a background sync — no more juggling cf target or hand-running cf env.

npm version license node install size types

InstallQuick StartCLIAPIFAQ


✨ Features

  • 🌍 Full-landscape sync — logs into CF once, walks region → org → space → app across every region you can reach
  • 🟢 App runtime metadata — snapshots include requested state, instance counts, and routes from cf apps
  • Partial + streaming readsread / regions / region commands return whatever is already known, even while a long sync is in progress
  • 🧭 Region org refreshorgs refreshes one region's org list without walking spaces or apps
  • 🗄️ Background DB binding syncdb-sync can collect VCAP_SERVICES.hana credentials for every cached app or one app selector in the background
  • 🧠 Smart fallbacks — runtime state first, last stable snapshot next, on-demand fetch as a last resort
  • 🧩 CLI & typed API — every command has a zero-config Node.js equivalent with full TypeScript definitions
  • 📦 Drop-in for other saptools — the output file is the shared source of truth for packages like @saptools/cf-xsuaa
  • 🪶 Small + boring — two deps (commander, ora), one-shot background workers only when requested, no resident daemon

📦 Install

# Global CLI
npm install -g @saptools/cf-sync

# Or as a dependency
npm install @saptools/cf-sync
# pnpm add @saptools/cf-sync
# yarn add @saptools/cf-sync

[!NOTE] Requires Node.js ≥ 20 and the official cf CLI on PATH (v8 recommended).


🚀 Quick Start

# 1. Export your SAP SSO credentials (only used during sync)
export SAP_EMAIL="[email protected]"
export SAP_PASSWORD="your-sap-password"

# 2. Sync every accessible region in parallel
cf-sync sync --verbose

# 3. Read the topology snapshot back from anywhere — CLI, script, or Node process
cf-sync read | jq '.regions[] | {key, accessible}'

# 4. Optionally collect HANA DB bindings for every cached app in the background
cf-sync db-sync
cf-sync db-read | jq '.metadata'

After the first topology sync, ~/.saptools/cf-structure.json is ready for the rest of your tooling. If you run db-sync, the HANA binding snapshot is stored separately.


🧰 CLI

🔄 cf-sync sync

Run a live sync and write a topology snapshot. A full sync replaces the snapshot; --only refreshes the listed regions and merges them into the existing snapshot without removing other cached regions.

cf-sync sync
cf-sync sync --verbose
cf-sync sync --no-interactive
cf-sync sync --only ap10,ap11,eu10

| Flag | Description | | --- | --- | | --verbose | Print progress lines | | --no-interactive | Disable the spinner (use in CI) | | --only <keys> | Sync only the listed region keys |

📖 cf-sync read

Print the best-available full structure as JSON — always succeeds as long as something has been synced before.

  • Returns runtime state while a sync is in progress
  • Falls back to the last stable snapshot otherwise
cf-sync read

🗺️ cf-sync regions

Print the list of regions (key + label + endpoint + accessibility).

  • Returns the default SAP CF catalog while a first sync is still running
  • Returns only synced regions with orgs once a snapshot exists
cf-sync regions

🎯 cf-sync region <key>

Print one region as JSON, fetching it on demand if it's missing.

cf-sync region eu10
cf-sync region eu10 --no-refresh

| Flag | Description | | --- | --- | | --no-refresh | Read cached data only, never call CF |

[!TIP] cf-sync region <key> is the fastest way to answer "what's in just this region right now?" without walking everything.

🧭 cf-sync orgs <region>

Refresh only the Cloud Foundry org names for one region and merge that list back into the shared topology snapshot.

  • Updates only the requested region
  • Preserves cached spaces/apps for orgs that still exist
  • Removes stale org entries from that region only
  • Adds newly discovered orgs with empty spaces until a targeted org, space, or full sync fills them in
  • Uses an isolated CF_HOME, so it does not clobber your interactive CF CLI target
cf-sync orgs ap10
cf-sync orgs eu10 --verbose

🧭 cf-sync org <region> <org>

Refresh exactly one Cloud Foundry org and merge every refreshed space/app in that org back into the shared topology snapshot.

  • Updates only the requested region/org
  • Preserves sibling orgs and regions already present in the snapshot
  • Fails without changing the stable snapshot when the requested org cannot be targeted
  • Uses an isolated CF_HOME, so it does not clobber your interactive CF CLI target
cf-sync org ap10 my-org
cf-sync org eu10 my-org --verbose

🔁 cf-sync space <region> <org> <space>

Refresh exactly one Cloud Foundry space and merge the latest app metadata back into the shared topology snapshot.

  • Updates only the requested region/org/space
  • Preserves sibling orgs and spaces already present in the snapshot
  • Uses an isolated CF_HOME, so it does not clobber your interactive CF CLI target
  • Can run while a full cf-sync sync is active; the merge is serialized through the same runtime-state lock
cf-sync space ap10 my-org dev
cf-sync space eu10 my-org app --verbose

🗄️ cf-sync db-sync [selector]

Start a detached background worker that collects VCAP_SERVICES.hana credentials.

  • with no selector: sync every app in the cached topology snapshot
  • with <app>: sync one uniquely named app from the cached topology snapshot
  • with region/org/space/app: sync one explicit app even if no topology snapshot exists yet
cf-sync db-sync
cf-sync db-sync orders-srv
cf-sync db-sync ap10/my-org/dev/orders-srv

[!IMPORTANT] cf-sync db-sync persists HANA credentials to local disk under ~/.saptools/. Treat that file like a secret.

📚 cf-sync db-read [selector]

Read the best available HANA binding snapshot as JSON.

  • cf-sync db-read returns the full runtime/stable DB snapshot view
  • cf-sync db-read <selector> returns one app binding view
cf-sync db-read
cf-sync db-read orders-srv
cf-sync db-read ap10/my-org/dev/orders-srv

🧑‍💻 Programmatic Usage

import {
  readDbAppView,
  readDbSnapshotView,
  resolveDbSyncTargetsFromCurrentTopology,
  runDbSync,
  findRegion,
  getRegionView,
  readRegionsView,
  readStructure,
  readStructureView,
  runSync,
  syncOrg,
  syncRegionOrgs,
  syncSpace,
} from "@saptools/cf-sync";

// Run a sync from Node (great for scheduled jobs)
const result = await runSync({
  email: process.env["SAP_EMAIL"] ?? "",
  password: process.env["SAP_PASSWORD"] ?? "",
  onlyRegions: ["ap10", "ap11"],
  interactive: false,
});
console.log(`${result.accessibleRegions.length} regions reachable`);

// Read the last snapshot
const structure = await readStructure();
const ap10 = structure ? findRegion(structure, "ap10") : undefined;
console.log(`${ap10?.orgs.length ?? 0} orgs in ap10`);

// Refresh only one region's org names without loading spaces/apps
const ap10Orgs = await syncRegionOrgs({
  regionKey: "ap10",
  email: process.env["SAP_EMAIL"] ?? "",
  password: process.env["SAP_PASSWORD"] ?? "",
});
console.log(ap10Orgs.orgNames.join(", "));

// Partial / on-demand reads
const view = await readStructureView();           // best-available full view
const regions = await readRegionsView();          // region list only
const eu10 = await getRegionView({                // one region, auto-fetch if missing
  regionKey: "eu10",
  email: process.env["SAP_EMAIL"],
  password: process.env["SAP_PASSWORD"],
});
console.log(eu10?.source); // "runtime" | "stable" | "fresh"

// Targeted org refresh
const refreshedOrg = await syncOrg({
  regionKey: "ap10",
  orgName: "my-org",
  email: process.env["SAP_EMAIL"] ?? "",
  password: process.env["SAP_PASSWORD"] ?? "",
});
console.log(refreshedOrg.org.spaces.map((space) => space.name));

// Targeted space refresh
const refreshedSpace = await syncSpace({
  regionKey: "ap10",
  orgName: "my-org",
  spaceName: "dev",
  email: process.env["SAP_EMAIL"] ?? "",
  password: process.env["SAP_PASSWORD"] ?? "",
});
console.log(refreshedSpace.space.apps.map((app) => app.name));
console.log(refreshedSpace.space.apps[0]?.requestedState);

// Resolve DB targets from cached topology or an explicit selector
const dbTargets = await resolveDbSyncTargetsFromCurrentTopology("orders-srv");
const dbResult = await runDbSync({
  email: process.env["SAP_EMAIL"] ?? "",
  password: process.env["SAP_PASSWORD"] ?? "",
  targets: dbTargets,
});
console.log(dbResult.snapshot.entries.length);

const dbView = await readDbSnapshotView();
const ordersDb = await readDbAppView("orders-srv");
console.log(dbView?.metadata?.status, ordersDb?.entry.bindings.length);

| Export | Description | | --- | --- | | runSync(options) | Drive a full/partial sync | | syncOrg({ regionKey, orgName, ... }) | Refresh one CF org and merge it into runtime/stable topology | | syncSpace({ regionKey, orgName, spaceName, ... }) | Refresh one CF space and merge it into runtime/stable topology | | readStructure() | Last stable snapshot, or undefined | | readStructureView() | Best-available full view with metadata | | readRegionsView() | Region list only, with fallbacks | | readRegionView(key) | One region from cached data | | getRegionView({ regionKey, ... }) | One region, fetching on demand if missing | | resolveDbSyncTargetsFromCurrentTopology(selector?) | Resolve all apps, one app name, or one explicit app selector for DB sync | | runDbSync({ email, password, targets, ... }) | Collect HANA DB bindings for the given app targets | | readDbSnapshot() | Last stable DB binding snapshot, or undefined | | readDbRuntimeState() | Current DB sync runtime state, or undefined | | readDbSnapshotView() | Best-available DB snapshot view with runtime metadata | | readDbAppView(selector) | One DB snapshot entry by app name or explicit selector | | findRegion(structure, key) | Look up a region by key | | findOrg(region, name) | Look up an org within a region | | findSpace(org, name) | Look up a space within an org | | cfAppDetails(context?) | Run cf apps and parse app state, instance counts, and routes | | findApp(space, name) | Look up an app within a space |


📁 Output Files

All state lives under your home directory:

~/.saptools/cf-structure.json     # last successful full sync (stable)
~/.saptools/cf-sync-state.json    # active runtime state, partial reads, sync metadata
~/.saptools/cf-sync-history.jsonl # append-only timeline of sync milestones for debugging
~/.saptools/cf-db-bindings.json   # last successful HANA binding snapshot (contains credentials)
~/.saptools/cf-db-sync-state.json # active DB binding runtime state
~/.saptools/cf-db-sync-history.jsonl # append-only DB sync milestones

cf-sync-history.jsonl is newline-delimited JSON. Each entry records a timestamped milestone such as lock acquisition, region traversal, runtime merges, recoveries, and final completion/failure so you can reconstruct where a sync got stuck.

cf-db-bindings.json is also newline-free JSON, but unlike the topology files it contains HANA credentials. Do not commit it, attach it to tickets, or paste it into logs.

{
  "syncedAt": "2026-04-18T00:00:00Z",
  "regions": [
    {
      "key": "ap10",
      "label": "Singapore",
      "apiEndpoint": "https://api.cf.ap10.hana.ondemand.com",
      "accessible": true,
      "orgs": [
        {
          "name": "my-org",
          "spaces": [
            {
              "name": "dev",
              "apps": [{ "name": "my-srv" }]
            }
          ]
        }
      ]
    }
  ]
}

[!IMPORTANT] Prefer the CLI read commands or the exported APIs over parsing these files directly — the on-disk format is an implementation detail.


❓ FAQ

No. SAP_EMAIL / SAP_PASSWORD are only used during sync, db-sync, and cf-sync region <key> when the region is missing locally. Pure read commands work offline.

As often as your CF topology changes in a way you care about — usually daily or weekly is plenty. cf-sync sync --only ap10,eu10 keeps hot regions fresh without walking everything.

cf-structure.json does not contain secrets, but it does list every org, space, and app you can reach — so it leaks your landscape's structure. Keep it out of public repos.

cf-db-bindings.json is more sensitive: it contains HANA credentials by design. Treat it like a secret and never commit it.

Those commands only act on the currently targeted region/org. cf-sync walks every region in one pass and gives you a unified, cached view — which is what every other saptools package consumes.


🛠️ Development

From the monorepo root:

pnpm install
pnpm --filter @saptools/cf-sync build
pnpm --filter @saptools/cf-sync typecheck
pnpm --filter @saptools/cf-sync test:unit
pnpm --filter @saptools/cf-sync test:e2e

The e2e suite hits live SAP BTP CF. Set CF_SYNC_E2E_ONLY=ap10,eu10 (plus SAP_EMAIL / SAP_PASSWORD) to restrict the regions it walks in CI.


🌐 Related


👨‍💻 Author

dongtran

📄 License

MIT


Made with ❤️ to make your work life easier!