CLICLItypeA command you run, rather than a dependency you import.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

@saptools/cf-hana

A command line and TypeScript API that runs SQL against the SAP HANA Cloud database bound to a Cloud Foundry app, addressed by a region/org/space/app selector.

Looking at the data a deployed app holds needs the credentials of its HANA binding, which sit in the app's VCAP_SERVICES. cf-hana reads that binding live on every connection and writes no credentials under ~/.saptools/; it does not read the snapshots of @saptools/cf-sync. Its query, tables, columns and count commands run over a pooled connection through the bundled hdb driver, with an opt-in read-only mode, a guard that blocks DROP, TRUNCATE, ALTER and an UPDATE or DELETE without a WHERE, and, for a CLI UPDATE, UPSERT, REPLACE, matched MERGE or DELETE, a copy of the affected rows saved before the write runs. Where the database accepts no connection from the developer's machine, it retries through a cf ssh port-forward opened on an app in the same org and space. The README calls the guard a convenience and not a security control.

Facts

Latestv0.7.0ReleasedLast pushLicenceMIT

Where it sits

Lives in
Provides
DebuggingCAPDebuggingcapabilitySeeing what running code did between an input arriving and a result coming out: which code ran, what the values were at each point, and why a value ended up as it did.
Runs
Cloud Foundry
Requires
SAP HANA CloudSAP HANA CloudartifactThe SAP HANA Cloud database bound to the target app, which every query runs against.SAP BTP, Cloud Foundry RuntimeSAP BTP, Cloud Foundry RuntimeartifactThe app whose HANA binding it reads live with cf env, and the space where it opens a cf ssh tunnel when a direct connection fails.Node.jsNode.jsoutside the atlasIt is a Node command line and library, and the README names the Node version it needs.

Who is behind it

Đông TrầnĐông TrầnPersonMaintains this
In the atlas: maintains 18, contributes to 18
@dongitran
maintainer

What it declares

Every artifact

Type
CLI
Origin
Independently maintained
Runs on
Cloud Foundry

As a CLI

Distributed through
npm

Connections4

Install

npm add @saptools/cf-hana

Derived from the package name. No recipe recorded; the README has the configuration.

Where @saptools/cf-hana runsYour SAP BTP subaccountBy its type: a CLI is run from a terminalcallsdocumentedrequiresdocumentedrequiresdocumentedrequiresYour terminal, actorYour terminalNode.js, systemNode.jssystem@saptools/cf-hana, module@saptools/cf-hanaSAP HANA Cloud, service instanceSAP HANA CloudSAP BTP, Cloud Foundry Runtime, service instanceSAP BTP, CloudFoundry Runtime
SAPnot SAP's, or the project's ownstated in the documentdocumented only

README

quoted from dongitran/saptools under MIT, read
dongitran/saptools/packages/cf-hana/README.mdOn GitHub

@saptools/cf-hana

Run SQL directly against SAP HANA Cloud databases bound to a Cloud Foundry app — addressed by a region/org/space/app selector.

@saptools/cf-hana opens live SAP HANA Cloud connections from Cloud Foundry app bindings. Pass a selector, get a connected, pooled client, and run SELECT / INSERT / UPDATE / DELETE / DDL. Bare app names use your active cf target and current CF session first, so a healthy local CF login does not require SAP password re-authentication.

Features

  • Selector-based connect — region/org/space/app or a bare app name.
  • Visible target provenance — every connecting CLI command confirms the resolved selector on stderr and labels ambient versus explicit targeting.
  • Credentials, handled for you — HANA bindings are read live from Cloud Foundry. Bare app names use the current CF session first; explicit selectors use isolated live authentication.
  • Parameterized queries — values always travel as bound ? parameters, never string-concatenated.
  • Connection pooling — pooled, reused connections; opt out with pool: false.
  • Transactions — transaction(work) commits on success, rolls back on throw.
  • Query-builder shorthands — selectFrom, count, insertInto, update, deleteFrom — query a table by name without writing SQL.
  • Schema introspection — list schemas, tables, and columns.
  • Table-name recovery — failed CLI queries for missing tables/views show nearby schema-local suggestions on stderr.
  • Privilege recovery — HANA error 258 identifies the active schema, technical user, binding, and any sibling bindings worth trying explicitly.
  • Local SQL history — direct SQL calls are appended to dated JSONL files under ~/.saptools/cf-hana/histories/ with five-day retention.
  • Write backups — CLI UPDATE, UPSERT, REPLACE, matched MERGE, and DELETE statements preserve pre-image rows before the write runs.
  • Compact CLI results — CLI SELECT/WITH output is compact CSV with bounded cells; truncated exact values are retained automatically for follow-up inspection.
  • Lossless CLI formats — query --format table|json|json-compact|csv provides exact cell values without changing the compact-CSV default.
  • Safety guard — opt-in read-only mode and a destructive-statement guard (blocks destructive DDL, unscoped UPDATE/DELETE, and unconditional matched deletes).
  • Typed results — query<TRow>() returns typed rows.
  • CLI + API — a cf-hana CLI and an ergonomic TypeScript API.

Installation

npm install @saptools/cf-hana
# or, for the CLI
npm install -g @saptools/cf-hana

Requires Node.js >= 20. The pure-JavaScript hdb driver is bundled as a dependency — there is no native build step.

Updates

Every command first checks npm for a newer @saptools/cf-hana (at most once an hour, one small request with a 2-second timeout) and, when one exists, installs that exact version with the package manager that owns the running binary and re-runs the command you typed on the new version. Both steps are announced on stderr; nothing is printed when the install is already current:

cf-hana: updating 0.6.0 -> 0.7.0 ...
cf-hana: updated to 0.7.0; re-running the command

If the install cannot complete, one stderr line gives the manual command and the command runs on the installed version; that version is not retried for a day. cf-hana self-update forces the check and install now; cf-hana self-update --check only reports.

ControlEffect
SAPTOOLS_AUTO_UPDATE=on|notify|offon (default) installs and re-runs; notify prints the manual command once per version; off never checks. Applies to every @saptools CLI.
CF_HANA_AUTO_UPDATEsame values, this CLI only; wins over the global variable
SAPTOOLS_UPDATE_INTERVAL_MINUTESminutes between checks (default 60; 0 checks on every run)
SAPTOOLS_NPM_REGISTRYregistry to check and install from (default: npm's configured registry, then npmjs)
SAPTOOLS_UPDATE_DEBUG=1explain on stderr why nothing happened

The updater switches itself off in CI (CI set), under NODE_ENV=test or NO_UPDATE_NOTIFIER, when the binary runs from a source checkout, an npm link or an npx cache, and inside the re-run itself. It never writes to stdout, never asks for input, never uses sudo, and never moves onto a prerelease. Its state lives in ~/.saptools/updates/.

Quick start

import { connect, query } from "@saptools/cf-hana";

// Open a reusable, pooled client for one CF app's HANA database.
const db = await connect("eu10/example-org/space-demo/app-demo");

const open = await db.query("SELECT ID, STATUS FROM ORDERS WHERE STATUS = ?", ["OPEN"]);
console.log(open.rows);

const total = await db.count({ schema: "ORDERS_APP", table: "ORDERS", where: { STATUS: "OPEN" } });

await db.transaction(async (tx) => {
  await tx.execute("UPDATE ORDERS SET STATUS = ? WHERE ID = ?", ["SHIPPED", 42]);
});

await db.close();

// One-shot: connect, run one query, close.
const rows = await query("app-demo", "SELECT COUNT(*) AS N FROM ORDERS");

The selector

Every entry point takes a selector as its first argument:

  • Explicit — region/org/space/app (e.g. eu10/example-org/space-demo/app-demo). Works without any cached topology.
  • Bare app name — app-demo. Resolved only against the active cf target (current org/space/API endpoint) and fetched with cf env app-demo using your existing CF session before any isolated re-auth fallback.

Every connecting CLI command prints the resolved selector to stderr. A bare selector is labeled as inherited from ambient cf target; an explicit selector is labeled as pinned. For the direct bare-app path, cf-hana verifies the app, API, org, and space embedded in the same VCAP_APPLICATION payload as the bindings, then reads cf target again. Missing or mismatched identity, including an A-to-B-to-A target race, is refused before a database connection is opened. If the ambient API cannot be mapped to a supported region, the notice says the region is unconfirmed instead of presenting current/... as a usable pin.

CLI

cf-hana query   <selector> <sql>            Run a single SQL statement
cf-hana tables  <selector> [schema]         List tables in a schema
cf-hana columns <selector> <schema.table>   List the columns of a table
cf-hana count   <selector> <schema.table>   Count rows in a table
cf-hana ping    <selector>                  Connect and measure round-trip latency
cf-hana info    <selector>                  Print the resolved connection metadata
cf-hana result  <command>                   Inspect saved query refs

Common options: --refresh (deprecated compatibility flag; binding discovery is already live), --role <runtime|hdi>, --binding <name> / --binding-index <n>, --timeout <ms>, --read-only, --allow-destructive, --limit <n>, --no-auto-limit, --tunnel, --refresh-tunnel (see Connectivity fallback). The query command also accepts --param <value> (repeatable), --cell-limit <n>, --save, --no-auto-save, --format <table|json|json-compact|csv>, --result-ttl-minutes <n>, and --refresh-metadata. tables and columns support the same four format values. CLI UPDATE, UPSERT, REPLACE, matched MERGE, and DELETE statements are backed up automatically before the write runs.

cf-hana query eu10/example-org/space-demo/app-demo "SELECT ID, STATUS FROM ORDERS WHERE STATUS = ?" \
  --param OPEN --read-only --save
cf-hana query app-demo "UPDATE ORDERS SET STATUS = ? WHERE ID = ?" \
  --param DONE --param 42
cf-hana tables app-demo
cf-hana columns app-demo ORDERS_APP.ORDERS
cf-hana ping eu10/example-org/space-demo/app-demo

Output formats and schemas

With no query --format, successful SELECT/WITH output remains compact CSV for backward compatibility. An explicit format is lossless at the cell level:

  • query --format json returns [{COLUMN: value, ...}].
  • query --format json-compact returns [value, ...] for a single-column projection and falls back to row objects for multiple columns.
  • query --format csv returns lossless RFC 4180 CSV.
  • query --format table returns a lossless aligned table.
  • tables --format json returns [{SCHEMA,TABLE,TYPE}]; its json-compact mode returns [TABLE, ...].
  • columns --format json returns [{COLUMN,TYPE,LENGTH,NULLABLE,POSITION}]; its json-compact mode returns [COLUMN, ...].

The existing uppercase catalog keys are unchanged. Query formats are available only for SELECT/WITH. --save cannot be combined with --format, which keeps JSON and CSV stdout valid machine output. The automatic row cap still applies unless changed with --limit or --no-auto-limit.

Compact query output and saved refs

For CLI SELECT and WITH statements, stdout is CSV. Bare reads return at most 100 rows by default; pass --limit <n> to request more, or --no-auto-limit to disable the automatic cap. Data cells display at most 128 Unicode characters by default; pass --cell-limit <n> to choose a value from 1 through 10,000.

When compact output actually shortens one or more cells, cf-hana automatically saves the exact returned rows and prints a concrete result show command with the generated ref to stderr. This does not add a ref= line to stdout. Disable implicit retention with --no-auto-save. If an implicit save exceeds the 256 MiB store ceiling or local storage is unavailable, the query still succeeds and the hint recommends --save or a larger --cell-limit.

Use explicit --save when you want a ref regardless of whether cells are shortened:

cf-hana query app-demo "SELECT ID, CONTENT FROM ORDERS" --read-only --save

Saved output starts with a control line, then CSV:

ref=q7f3a9c2b
ID,CONTENT
1,first 128 visible characters

The ref is not a CSV column. Exact returned rows are stored under ~/.saptools/cf-hana/results/ for 7 days by default. Only returned rows are stored; rows beyond the selected --limit are not fetched or saved.

Follow-up commands:

cf-hana result show q7f3a9c2b
cf-hana result show q7f3a9c2b --row 1
cf-hana result show q7f3a9c2b --row 1 --column CONTENT --length 1000
cf-hana result show q7f3a9c2b --row 1 --column PAYLOAD --path /items/0
cf-hana result search q7f3a9c2b "ready"
cf-hana result export q7f3a9c2b --row 1 --column CONTENT --output content.txt
cf-hana result list
cf-hana result prune
cf-hana result clear

--save is available only for SELECT and WITH statements and remains a hard failure if the result cannot be stored. The programmatic API keeps returning full-fidelity QueryResult values and does not write result refs.

Saved results

A saved result is kept for 7 days, then removed by the next cf-hana command that touches the store (or immediately by cf-hana result prune). Nothing else expires it and nothing caps how many accumulate. Files are written 0600 inside 0700 directories.

Pruning only ever removes a result that has expired, or a ref directory it has verified is empty. One this version cannot read — a permission error, a partial write, or a format a newer cf-hana wrote — is deliberately left on disk and reported by cf-hana result prune on stderr, so a downgrade or a stale global install cannot destroy saved results it merely fails to understand. cf-hana result show says which of those happened rather than reporting a readable file as missing.

The trade-off is that a result this version cannot read is then kept indefinitely: no TTL reaches it, and the only way to reclaim it today is cf-hana result clear, which removes every saved result it can see — it does not reclaim a leftover <ref>.tmp-<pid> directory from an interrupted save.

Insufficient-privilege guidance

When HANA reports error code 258, or the equivalent insufficient privilege message without a numeric code, cf-hana keeps the original failure and exit status and adds a stderr hint. The hint identifies the schema, role-selected technical user, current binding, and other named HANA bindings already found on the app. It suggests an explicit --binding <name> or another app/full selector. It never retries the SQL automatically under another database user.

Invalid table/view suggestions and metadata cache

When cf-hana query fails with a likely HANA invalid table, view, or catalog object error, the CLI keeps stdout empty/parseable and prints the original error plus a small Did you mean: list to stderr. Suggestions are based on objects in the active connection schema and include physical tables from SYS.TABLES and views from SYS.VIEWS; tables output remains table-only for compatibility.

To avoid repeatedly reading catalog metadata after typo failures, cf-hana stores only schema, object name, and object type under:

~/.saptools/cf-hana/metadata/

Metadata cache files are private (0700 directories, 0600 files), written atomically, and expire after exactly 30 minutes. The cache key is derived from non-secret connection identity: selector, app name, host, active schema, role, driver, and selected binding name/index. It does not include passwords, certificates, tokens, SQL parameter values, result rows, or table data, and malformed cache files are treated as misses. Pass --refresh-metadata to bypass this cache for a query. The legacy --refresh flag is accepted for compatibility but does not bypass the metadata cache and is not a credential-cache control because binding discovery is already live. If metadata lookup or cache I/O fails, cf-hana preserves the original query error and simply omits suggestions.

Programmatic API

ExportPurpose
connect(selector, options?)Open a reusable, pooled HanaClient.
query(selector, sql, params?, options?)One-shot: connect, query, close.
withConnection(selector, work, options?)Run work with a client that auto-closes.
HanaClientquery, execute, backupWriteStatement, selectFrom, count, insertInto, update, deleteFrom, transaction, listSchemas, listTables, listCatalogObjects, listColumns, explain, close.
createDriver, formatResult, build*Lower-level building blocks.

ConnectOptions highlights: role (runtime | hdi), bindingName / bindingIndex, readOnly, allowDestructive, autoLimit, queryTimeoutMs, connectTimeoutMs, deprecated refresh, pool.

HanaClient.info additively exposes optional target and binding provenance for programmatic checks: selectorSource, regionConfirmed, selectorCanBePinned, bindingName, bindingIndex, and availableBindingNames. Library connections remain silent; only the CLI writes the resolved-target notice to stderr.

Credentials

Credential discovery is live-only and does not read @saptools/cf-sync snapshots or ~/.saptools/cf-db-bindings.json.

  • Bare app selectors (app-demo) read the active cf target, preserve its exact validated API endpoint, and first run cf env app-demo with your current CF session. This path does not require SAP_EMAIL or SAP_PASSWORD while the current session is healthy.
  • A successful direct cf env is accepted only if a second cf target read confirms the same API endpoint, org, and space.
  • If that direct bare-app call fails with an auth/session error, cf-hana falls back to an isolated temporary CF_HOME, runs cf api <current-endpoint>, authenticates with SAP_EMAIL / SAP_PASSWORD (or the programmatic email / password options), targets the current org/space, and reads cf env.
  • Explicit region/org/space/app selectors always use isolated live authentication. Region keys support current SAP CF technical keys, including indexed regions such as eu10-005, eu20-001, us10-002, and China endpoints such as cn40 on platform.sapcloud.cn.

The legacy refresh / --refresh option is retained only for compatibility and has no credential-cache meaning; binding discovery is already live. Use --refresh-metadata when you specifically want to bypass the table/view suggestion metadata cache. Credential resolution writes no binding credentials under ~/.saptools/.

Cloud Foundry shell-outs use 60-second bounded timeouts and retry transient timeout/network failures. HANA connection and query timeout defaults are also 60 seconds unless overridden with --timeout or API options.

SQL history

Successful direct SQL calls are appended to daily JSONL files:

~/.saptools/cf-hana/histories/YYYY-MM-DD.jsonl

Each entry includes the timestamp, package version, selector, app name, schema, role, operation (query or execute), statement kind, SQL text, parameter count, row count, truncation flag, and elapsed time. Parameter values, credentials, certificates, and result rows are not stored.

History retention runs opportunistically after each append and deletes dated history files older than five days. Helper-driven catalog SQL such as tables, columns, and table/view suggestion metadata reads is not recorded as user SQL history.

Write backups

When cf-hana query receives a supported write, it first builds and runs a pre-image SELECT:

  • UPDATE <target> SET ... WHERE ... becomes SELECT * FROM <target> WHERE ....
  • UPSERT <target> VALUES ... WHERE ... becomes SELECT * FROM <target> WHERE ....
  • REPLACE follows the same plan as its HANA UPSERT synonym.
  • A reliably parsed matched MERGE INTO uses a correlated EXISTS query for the matched target rows. If the target is unambiguous but an exact matched set is not, cf-hana backs up the whole target table.
  • DELETE FROM <target> WHERE ... becomes SELECT * FROM <target> WHERE ....

UPSERT/REPLACE subquery forms use a conservative whole-target pre-image. Insert-only MERGE statements need no pre-image. For the supported write forms above, cf-hana refuses execution if it cannot identify a trustworthy target, derive a safe pre-image, preserve the backup within the 256 MiB ceiling, or write the backup files. This refusal cannot be overridden with --allow-destructive.

The backup is saved before the write runs:

~/.saptools/cf-hana/backups/YYYYMM/
  <region-org-space-app>-<operation>-<timestamp>.sql
  <region-org-space-app>-<operation>-<timestamp>.statement.sql
  <region-org-space-app>-<operation>-<timestamp>.json

The main .sql backup file contains CSV-formatted rows returned by the derived SELECT, matching the requested region/org/space/app/action/timestamp naming shape. The companion .statement.sql file contains the original write statement, and .json contains non-secret metadata for auditability. Backup files are not deleted by cf-hana; clean them up manually when they are no longer needed. The backup path is printed to stderr so stdout remains parseable. Backup directories use mode 0700; statement, CSV, and metadata files use mode 0600.

Safety

  • Read-only mode (readOnly / --read-only) rejects every DML and DDL statement.
  • Destructive guard blocks DROP / TRUNCATE / ALTER and UPDATE / DELETE without a top-level WHERE, plus unconditional matched MERGE DELETE, unless allowDestructive / --allow-destructive is set.
  • Single statement per call is an enforced, guarded property: a SQL argument containing more than one genuine top-level statement (separated by a real, non-quoted, non-commented ;) is refused unconditionally — not overridable by --allow-destructive or --read-only. CREATE PROCEDURE/ FUNCTION/TRIGGER definitions and DO anonymous blocks are exempted when chained back to back, since their BEGIN/END bodies legitimately contain internal semicolons — but each chunk's own header (its name, then everything up to its first BEGIN) must contain no top-level ; and no keyword, from a fixed disqualifying set, that could lead an independent statement, and its body's BEGIN/END nesting must genuinely balance and reach either the true end of the input or the start of another such definition. A reordered or partial BEGIN/END, a statement skipped over on the way to a later BEGIN, and content appended after a real body's own END (whether trailing the last definition or sandwiched between two), are all refused; only chaining several independently well-formed definitions back to back is allowed. Known, narrow limitations (see CHANGELOG for full detail): a TRIGGER header may legitimately reference INSERT/UPDATE/DELETE as its own event keywords (BEFORE INSERT, AFTER UPDATE, ...), so those three are not disqualifying specifically for TRIGGER; a chunk disguised as CREATE TRIGGER, chained after a separate legitimate definition, referencing one of them with no BEFORE/AFTER/INSTEAD OF prefix at all is not caught by this check. Separately, the disqualifying keyword set is a fixed list (not every keyword that could lead a dangerous statement in general — GRANT/REVOKE are notable omissions); this is not specific to the carve-out, since a bare, semicolon-free statement leading with any keyword this guard's classifier does not recognize is already treated as non-destructive regardless of any routine/trigger wrapper.
  • Auto-limit appends a LIMIT to bare SELECT statements (default 100); QueryResult.truncated reports when it clipped the result. Disable with autoLimit: false / --no-auto-limit.

The guard is a convenience, not a security control: always pass values as bound parameters.

Connectivity fallback

HANA Cloud instances are frequently IP-allowlisted to only accept connections from inside the same Cloud Foundry landscape. When a direct connection fails in a way that means the initial socket could never be established — not an authentication, privilege, or query failure — cf-hana can retry through an SSH port-forward opened via cf ssh against another app in the same org/space, which usually can reach the host even when your machine cannot.

  • auto (default): tries the direct connection first, with zero added behavior when it succeeds. Only a classified connectivity failure triggers the fallback: discover a jump-host app (the target app itself, then a few other started apps via cf apps), open a local port-forward, and retry through 127.0.0.1.
  • --tunnel: skips the direct attempt and connects via a tunnel immediately — for a host already known to be unreachable directly, so you are not paying the connect-timeout cost (up to 60s) on every invocation.
  • --refresh-tunnel: bypasses a cached/live tunnel and forces a fresh establishment attempt.

There is deliberately no flag to disable this capability: a tunnel attempt only ever engages after a direct failure (or when explicitly requested via --tunnel), and on total failure it rethrows the original connection error unchanged, so it can only help or no-op.

The live tunnel is reused — both across every connection this CLI's own pool opens in one invocation, and across separate cf-hana invocations run in a row against the same host (this CLI's realistic dominant usage pattern, e.g. an AI agent running several queries back to back) — instead of re-negotiating SSH on every single command. State lives at ~/.saptools/cf-hana/tunnel/ (mode 0700/0600, no credentials). A cached tunnel is closed immediately, regardless of its remaining lifetime, the moment a later invocation targets a different Cloud Foundry org — a different client's landscape never keeps an unattended live SSH path open just because its keepalive has not lapsed.

In the worst case (a silently-dropping network path, so the direct attempt runs its full connect timeout, and no candidate app works either), auto mode takes roughly 15s (direct, capped once a tunnel fallback is available — see below) + 25s (tunnel budget) ≈ 40s before surfacing a final error — bounded and predictable, not open-ended. Use --tunnel to skip the direct cost entirely for a host you already know is unreachable.

Whenever a tunnel fallback is available at all (auto mode, the default), the direct attempt's own timeout is capped at the tunnel side's own per-candidate ceiling (15s by default) even if a longer timeout is configured — a silently-hanging (rather than actively refused) direct connection would otherwise consume its entire configured timeout before the tunnel path got a chance at all. This only ever shortens an otherwise-hanging attempt; it never affects a host that refuses quickly, or a configured timeout that is already shorter.

cf-hana also disables SAP HANA Cloud's reactive mid-auth redirect (used for per-node/pod locality routing) on every connection, direct or tunneled: without this, a multi-node HANA Cloud instance can redirect an already-established connection to a different internal hostname that isn't reachable outside SAP's own network, silently abandoning a working SSH tunnel for a fresh, untunneled connection that fails for the same reason the original direct connection did. The original bound host is always a real, working endpoint for your binding, so this has no effect on which schema, user, or data you reach.

Known limitations
  • Cross-org reaper and concurrent processes. Every connection attempt closes any cached tunnel tagged for a different Cloud Foundry org than the current target, regardless of remaining lifetime — intentional hygiene so switching orgs never leaves a stale tunnel open. On a shared machine, this can also close another, unrelated, concurrently-running invocation's active tunnel to a different org; there is no portable, low-cost way to detect "is this port actively in use by someone else" from outside that TCP session.
  • Stale-tunnel reaping trusts the recorded pid. The reaper terminates a dead or expired tunnel's process by its recorded pid, with no additional identity check (e.g. confirming the pid still refers to a cf ssh process). On a long-uptime machine, pid reuse could in principle target an unrelated process; this is a narrow race not worth a platform-specific check for.
  • Programmatic (non-CLI) pool usage can strand idle connections. The connection pool reuses the most-recently-released idle connection first (LIFO); a long-lived process that opens a burst of connections and then settles into serial one-at-a-time use can leave earlier connections idle-but-unreaped for the pool's lifetime. The CLI itself always drains its pool at the end of every command, so this only affects long-lived programmatic use of the library.
  • Pre-write backups are never pruned. Unlike SQL history (5-day retention) and saved query results (TTL plus result prune/result clear), files under ~/.saptools/cf-hana/backups/ accumulate indefinitely with no retention policy or cleanup command.

Requirements

  • Node.js >= 20.
  • A HANA binding reachable from your network. Resolving a bare app name needs the Cloud Foundry CLI and an active cf target. Isolated fallback or explicit selectors additionally need SAP_EMAIL / SAP_PASSWORD.

Development

pnpm --filter @saptools/cf-hana build
pnpm --filter @saptools/cf-hana lint
pnpm --filter @saptools/cf-hana typecheck
pnpm --filter @saptools/cf-hana test:unit
pnpm --filter @saptools/cf-hana test:e2e:fake

The live e2e suite (test:e2e:live) needs real SAP_EMAIL / SAP_PASSWORD and a CF_HANA_E2E_TARGET selector pointing at a HANA-bound app.

License

MIT

MIT, read

History

Latest

v0.7.0

Changed 1, Fixes 10, Other 3

Notes
Fixed
  • Pruning no longer deletes saved results it cannot read. pruneResultSessions runs at the head of every save, read and list, and it treated four distinct outcomes — file absent, I/O error, invalid JSON, unrecognized shape — as one "missing" answer, then rm -rf'd the directory. So a nominally read-only result list destroyed a valid unexpired session whose only difference was a newer version, and one that was merely unreadable at that moment (a permission error, with the data fully intact). Because the self-updater makes mixed versions on one machine normal, that also meant an older cf-hana silently wiped a store a newer one had written.

    Reads are now classified, and only an expired session or an empty ref directory is removed. A manifest that cannot be read or recognized is left exactly where it is and counted as retained; a ref directory holding files under names this version does not know is retained too.

  • A saved result with a damaged expiresAt is no longer immortal. Date.parse returns NaN for it and NaN <= now is false, so such a session survived every prune for ever and still read back. Expiry now falls back to createdAt plus ttlMinutes, and a session whose age cannot be established by either route is treated as expired.

  • One undeletable directory no longer takes the whole store offline. Deletion is attempted per session, and the housekeeping prune is best-effort, so result show and result list keep working on intact sessions even when the results directory cannot be listed. result show also enforces the TTL itself instead of relying on that prune having succeeded.

  • result show now says which of the three things went wrong. An unreadable manifest and one written in a newer format used to report "Saved result not found or expired", which was untrue; both now name the file, and a newer format says it was left in place. Added the RESULT_UNREADABLE error code.

  • result list reports the ref that actually resolves. The listed ref came from inside the manifest while lookup uses the directory name; when those disagreed, list advertised a ref that result show rejected. Expired sessions that prune could not remove are omitted rather than shown.

  • Pruning no longer deletes a saved result whose expiry it cannot establish. resolveExpiryMillis previously fell back to "treat as expired", so a manifest with damaged timestamps had its rows deleted even though they were perfectly readable — and a version that changed only the timestamp encoding (a Temporal ZonedDateTime, an ISO week date, epoch millis as a string, or canonical ISO with surrounding whitespace, which Date.parse does not trim) would have had its data destroyed by an older binary. Such a session is now retained and reported, like any other manifest this version cannot fully interpret. One rule now covers every case: only an expired session with a resolvable date, or a ref directory verified to be empty, is ever deleted.

  • A ttlMinutes too large to date no longer produces an immortal session. The bound is what a Date can hold, not what a float can hold: Number.isSafeInteger admits a value whose product with 60000 reaches 5.4e20, while a Date holds at most ±8.64e15 ms — so createdAt + ttlMinutes could yield a finite expiry no clock will ever reach, which survived every prune and was not even counted. A derived expiry beyond the Date range is now treated as unresolvable, hence retained.

  • result prune names the refs it left in place instead of only counting them. A retained session is omitted from result list and no command removes one, so the ref is the only way to find the file.

  • result prune exits non-zero when it could not delete an expired session. It is the only machine-readable health signal the store has, and reporting a partial sweep as success meant if cf-hana result prune; then … could never detect a store it had failed to clean.

  • PruneOutcome is now { removed, failed, retainedRefs }. retained was a count the user could not act on; the refs replace it.

Changed
  • pruneResultSessions returns { removed, retained, failed } instead of a bare count. result prune still prints removed=N as its only stdout line and reports retained and undeletable counts on stderr. The type is internal to the CLI: index.ts deliberately exports no part of the result store.
Known limitations
  • A leftover <ref>.tmp-<pid> directory from an interrupted save is invisible to result list, result prune and result clear, and no TTL reaches it. Reclaim it by hand.
  • A retained session is reported by result prune but not listed, and there is no result rm; result clear is the only in-tool removal and it removes everything it can see.
  • result list omits a session it could not read without saying so. Only result prune reports those.
  1. v0.6.0

    Other 1

    Notes
    • Self-updating. Every command now checks npm for a newer @saptools/cf-hana (at most once an hour) and, when one exists, installs that exact version and re-runs the command on it, announcing both steps on stderr. SAPTOOLS_AUTO_UPDATE=on|notify|off controls it (see the README's Updates section); it is off by itself in CI, in tests, from a source checkout and inside the re-run. New cf-hana self-update [--check] forces the check and install now. --version now reads package.json at runtime. Both come from the private, build-time-bundled @saptools/core package; the published tarball gains no runtime dependency.
  2. v0.5.4

    Other 4

    Notes
    • Security fix: 0.5.3's routine/anonymous-block carve-out (isRoutineDefinition/isAnonymousBlock) checked only that a top-level BEGIN and END existed somewhere in the string, not that they were correctly paired, ordered, or preceded only by plausible header content — a bare keyword-existence check, not a structural one. Two independent exploit families reopened the exact zero-flags bypass 0.5.3 exists to close, in normal mode with no flags required at all:
      1. A reordered/partial body, e.g. CREATE PROCEDURE p AS DELETE FROM t WHERE id=1; DROP TABLE other; END BEGIN (and the DO equivalent).
      2. (Found by independent review, after this release's first cut already shipped internally) the search for a routine/block's first BEGIN applied no validation to what it skipped over on the way there — it would skip straight past an entire independent, ;-separated statement to reach a later, genuinely well-formed BEGIN ... END, exempting the smuggled statement along with it, e.g. CREATE PROCEDURE p(x INT); DROP TABLE other; SELECT 1 BEGIN END. Fixed by depth-tracking BEGIN/END nesting the same way this file already depth-tracks parentheses, and requiring the region between a chunk's header keywords and its own first BEGIN to contain no top-level ; and no keyword that could lead an independent, dangerous statement from a fixed set (SELECT/CREATE/DROP/ALTER/TRUNCATE/RENAME/ COMMENT/WITH/CALL/MERGE/UPSERT/REPLACE, plus INSERT/ UPDATE/DELETE for every header type except TRIGGER, since a trigger header legitimately references those three as its own event keywords, e.g. BEFORE INSERT — MERGE/UPSERT/REPLACE have no such legitimate role in a trigger header and stay disqualifying for every type). The region is also required to skip exactly the routine/trigger's own name first (a bare or quoted identifier, optionally schema-qualified) before that keyword check runs at all, since a legitimately-named routine can coincide with one of those keywords (e.g. an unquoted replace) without being a smuggled statement — a single identifier token can never itself contain a real top-level ; or a second keyword, so this cannot be used to also skip real smuggled content. This closes the reordering bypass, a stray END with no preceding BEGIN, both keywords appearing early with real content trailing the close, and both exploit families above. As a direct consequence, 0.5.3's own disclosed residual limitation — content genuinely appended after a routine or anonymous block's own real END — is also closed, for both CREATE PROCEDURE/FUNCTION/TRIGGER and DO blocks. Chaining several independently-legitimate definitions back to back (e.g. two CREATE PROCEDURE statements in one call) remains allowed, since defining a routine is not itself destructive. Disclosed, narrow residual limitation: because TRIGGER headers are exempted from the INSERT/UPDATE/DELETE part of that keyword check (to avoid rejecting ordinary trigger event clauses), a chunk that specifically disguises itself as CREATE TRIGGER and references one of those three verbs with no ; and no BEFORE/AFTER/INSTEAD OF prefix at all in its header (e.g. CREATE TRIGGER t2 DELETE FROM customers BEGIN END, chained after a separate legitimate definition so a real ; exists for the check to matter at all — a fully standalone, semicolon- free instance of this shape is already unaffected by this check either way, since hasMultipleStatements never flags a string with no top-level ; regardless) is not rejected. Distinguishing a legitimate trigger event keyword from a bare one requires validating it is actually preceded by a trigger-timing keyword, which edges into full SQLScript-header grammar parsing — out of scope for this fix, the same way parsing a routine body's own contents is out of scope for the carve-out itself. This is narrower than either exploit family above: it requires specifically choosing TRIGGER (not PROCEDURE/FUNCTION/ DO) as the disguise, using specifically INSERT/UPDATE/DELETE (not any other keyword), chained with a real ; elsewhere in the input.
    • Separately noticed while verifying the fix above, not fixed here: the header-disqualifying keyword set above is a fixed list derived from this file's own existing DDL/DML/SELECT/WITH/CALL vocabulary, not every keyword that could lead an independent dangerous statement in general — GRANT/REVOKE are notable omissions. This is not unique to the routine/block carve-out: a bare, semicolon-free single statement leading with an unrecognized keyword (GRANT, REVOKE, or anything else classifyByKeyword does not recognize) is already treated as non-destructive by this guard's existing classifier, with or without any routine/trigger wrapper around it, since only a statement's own leading keyword and WHERE-scope are inspected. Extending the classifier's keyword vocabulary to cover GRANT/REVOKE (and auditing for other gaps of the same shape) is a separate, broader task than the routine-header validation this release adds, and is not attempted here.
    • Security fix, pre-existing since 0.5.1/0.5.2, broader than previously disclosed: the WITH-CTE-list parser (cteListEndIndex) did not fail closed once a CTE list was itself well-formed — if whatever followed it was neither a comma nor a recognizable keyword (e.g. stray punctuation), it returned a defined result with an empty "" keyword rather than undefined, which slipped past the guard's unresolved-WITH fail-closed check. 0.5.3's changelog disclosed a narrower version of this gap specific to a semicolon placed mid-CTE-list; this follow-up review found the same defect is reachable with no semicolon involved at all (WITH a AS (SELECT 1 FROM DUMMY) !!! DELETE FROM customers). Fixed by requiring the character immediately after a completed CTE list to be keyword-shaped (alphabetic) before treating the resolution as successful; otherwise it now fails closed exactly like an already-malformed CTE list. An unrecognized-but-alphabetic continuation (e.g. EXPLAIN) is unaffected and continues to resolve and classify exactly as before.
    • The hand-maintained zero-width/invisible-character allowlist (5 code points, added in 0.5.3) is replaced with a Unicode-property match (\p{Cf}, the standard "Format" general category), closing several realistic gaps it missed — including left-to-right/right-to-left marks, soft hyphen, and several invisible math-layout characters — instead of only the handful of code points a prior session happened to notice. Verified empirically to match every previously-listed code point, match none of A-Z/0-9/standard SQL punctuation, and not create any way to hide real trailing content.
  3. v0.5.3

    Other 4

    Notes
    • Security fix: the safety guard and pre-write backup planner only ever looked at a SQL string's leading statement — nothing checked whether the string contained a second, ;-separated statement after it. A properly WHERE-scoped write (exactly the kind of statement the guard is designed to wave through) could carry an entirely separate, unscoped statement (DELETE FROM t WHERE id=1; DROP TABLE other) past the guard with no flags required at all, in both normal and --read-only mode. Until this release, the only thing preventing a smuggled second statement from actually running was SAP HANA's own rejection of multi-statement text — behavior this tool never guaranteed and must not be understood to rely on. cf-hana now refuses any SQL argument containing more than one genuine top-level statement, unconditionally — not overridable by --allow-destructive or --read-only — via a structural, quote/comment/ paren-aware scan for a real (not string-literal, not commented-out, not nested) ; separator, matching the same "cannot safely determine the statement's true shape, refuse rather than guess" precedent already established for an unresolvable WITH clause. The pre-write backup planner applies the identical check before doing anything else, since it runs on every CLI statement before the main guard does and would otherwise fold a smuggled statement straight into its derived backup SELECT.
    • CREATE PROCEDURE/FUNCTION/TRIGGER definitions that have a real BEGIN/END body — and, separately, HANA SQLScript anonymous DO [(...)] BEGIN ... END blocks — are exempted from this check, since a genuine routine/block body legitimately contains many internal, top-level-looking semicolons that are not additional smuggled statements. An independent review caught that the first implementation of this exemption checked only the leading keyword pair (e.g. CREATE PROCEDURE) with no body required at all, which would have let a bare CREATE PROCEDURE p; DROP TABLE other skip the multi-statement check entirely with no flags required — fixed before release by requiring an actual top-level BEGIN and END to be present. Disclosed, intentional residual limitation: the exemption still covers the entire routine-creation statement once a body is confirmed present, so content genuinely appended after such a body's own END (e.g. CREATE PROCEDURE p AS BEGIN ... END; DROP TABLE other) is not caught by this check either — a full SQLScript BEGIN/END-nesting-aware parser would be required to find where the routine body truly ends, which is out of scope for this fix. This narrows a previously-universal gap down to one specific, harder-to-reach statement shape; it does not close it completely.
    • Also caught by that same review and fixed before release: a lone zero-width space or similar invisible Unicode formatting character trailing an otherwise ordinary single statement's ; — a realistic artifact of pasting SQL out of a chat app, word processor, or web page — was being treated as a smuggled second statement and incorrectly refused. An unclosed top-level parenthesis before a genuine ; separator (itself already invalid SQL) also defeated detection and the existing WHERE-scope check simultaneously; it is now treated the same as an unresolvable WITH clause — refused outright, since the statement's real shape can't be determined.
    • Separately noticed while verifying this fix, not fixed here: the 0.5.2 WITH-CTE-list parser (cteListEndIndex) does not fail closed for a ; placed inside a multi-CTE list (between two CTE definitions rather than after the whole list) — it returns a defined result with an empty keyword instead of undefined. This is not a live guard bypass on its own (any input reaching this path also has a genuine top-level ;, which this release's new check independently catches), but it is a real, pre-existing gap in the 0.5.2 parser worth a future, narrowly-scoped fix.
  4. v0.5.2

    Other 1

    Notes
    • Security fix: the 0.5.1 fix for WITH-prefixed guard bypasses shipped with its own gap — a WITH statement whose CTE name was a double-quoted identifier (e.g. WITH "x" AS (...) DELETE FROM ...) was misclassified as unknown instead of resolving to its real trailing statement, and that unknown classification was previously treated as non-destructive and exempt from the automatic pre-write backup. A quoted-CTE-name write with no WHERE clause reached the database with no client-side block at all, and no backup was attempted for a quoted-CTE-name write of any shape. Anyone who upgraded to 0.5.1 believing that release fully closed the WITH guard-bypass class should know it did not, quite yet — this release does. The parser now correctly recognizes a quoted CTE name instead of skipping past it as if it were blank space; a genuinely unparseable WITH statement (for any reason, not only quoting) is now also treated as destructive by the safety guard and refuses the pre-write backup outright, rather than silently falling back to "not destructive, no backup" as it did before.
  5. v0.5.1

    Other 8

    Notes
    • Security fix: a WITH-prefixed write (e.g. WITH x AS (...) DELETE FROM ...) and a bare CALL <procedure>() both previously slipped past the --read-only and destructive-statement guards entirely — the former was always classified as a harmless SELECT regardless of what it actually did, and the latter was never classified as destructive at all, so it ran with no --allow-destructive confirmation even in the default (non-read-only) mode. Both are now correctly classified and guarded. Anyone relying on --read-only or the destructive-statement guard as a hard trust boundary should treat this as the release that closes that gap. The automatic pre-write backup now also correctly recognizes and protects a WITH-prefixed UPDATE/DELETE/UPSERT/MERGE, matching what a non-WITH equivalent write already produced.
    • Disable SAP HANA Cloud's reactive mid-auth redirect on every connection (direct and tunneled): on a genuine multi-node HANA Cloud instance, the server can redirect an already-established connection to an internal per-node hostname that isn't reachable outside SAP's own network, which previously defeated the SSH-tunnel fallback by silently abandoning the tunnel for a fresh, untunneled connection that failed the same way the original direct connection did.
    • Fix the SSH-tunnel fallback's candidate discovery calling cf apps (measured at up to ~20s against a large space) before ever trying the already-known target app or a cached "last worked" app, which could starve the entire shared fallback budget before a working candidate got a chance. Known candidates are now always tried first; cf apps is only called if none of them work, and is bounded to whatever budget remains rather than retried on its own separate timeout/retry policy.
    • Cap the direct-connect attempt's own timeout to the tunnel fallback's per-candidate ceiling whenever a fallback is available, so a silently hanging (rather than actively refused) direct connection can no longer consume its full configured timeout before the tunnel path gets a chance — lowering the documented worst-case latency from ~85s to ~40s.
    • Fix --refresh-tunnel leaking the SSH process it superseded; the old tunnel is now terminated instead of running until its keepalive elapses.
    • Surface a failed tunnel candidate's actual stderr (e.g. "SSH disabled for this app") via the existing connectivity status output on stderr, instead of discarding it silently.
    • Fix a freshly-established or reused tunnel being cached as usable even when its own post-connect setup failed in a way that looked like a dropped connection rather than a genuine, actionable HANA rejection.
    • Bound how long a process waits for another concurrent invocation to finish establishing the same host's tunnel, so the entire shared deadline can no longer be spent waiting on a leader whose owning process has already died.
  6. v0.5.0

    Other 3

    Notes
    • Add an SSH-tunnel fallback for HANA Cloud hosts unreachable directly (e.g. IP-allowlisted landscapes): on a classified connectivity failure, discover a jump-host app in the same org/space via cf apps, open a local cf ssh -L port-forward, and retry through it. Zero behavior change when the direct connection succeeds.
    • Add --tunnel to skip the direct attempt and connect via a tunnel immediately, and --refresh-tunnel to bypass a cached/live tunnel and force a fresh attempt. No disable switch: the fallback can only help or no-op, and rethrows the original connection error unchanged on total failure.
    • Persist and reuse the live tunnel under ~/.saptools/cf-hana/tunnel/ across every connection this CLI's pool opens and across separate invocations run in a row against the same host, with a race-free concurrent-establishment marker and automatic reaping of dead or cross-org tunnels.
  7. v0.4.0

    Other 6

    Notes
    • Back up REPLACE and matched MERGE INTO pre-images, cap backup size, and refuse those statements when their target or safe pre-image cannot be derived.
    • Confirm every resolved CLI target on stderr, distinguish ambient from explicit selectors, and verify direct cf env identity against the ambient target.
    • Add actionable insufficient-privilege guidance with schema, technical user, current binding, and sibling binding names without automatic retries.
    • Auto-save exact rows only when compact CSV shortens cells, add --no-auto-save, and make implicit storage failures non-fatal.
    • Add lossless query --format table|json|json-compact|csv, flat catalog name output, and exact per-command JSON schemas in help while preserving defaults.
    • Scope metadata suggestions by binding identity and document the existing bounded CF retries and 60-second timeout defaults.
  8. v0.3.5

    Other 1

    Notes
    • Normalize HANA BOOLEAN result columns that arrive from the driver as 1/0 into JavaScript true/false values before CLI/API formatting and saved refs.
  9. v0.3.4

    Other 1

    Notes
    • Keep explicit selector region lookup map-backed so unknown technical keys fail before isolated authentication unless they are added to the maintained SAP CF region catalog.
  10. v0.3.3

    Other 4

    Notes
    • Fix Cloud Foundry region resolution for indexed SAP regions such as eu10-005, representative eu20/us10 indexed endpoints, and China platform.sapcloud.cn endpoints.
    • Keep bare CLI selectors on the core current-session path so healthy cf env <app> calls do not require isolated SAP re-authentication.
    • Preserve and validate the current CF API endpoint for auth fallback, reject unsafe endpoint shapes, and keep binding discovery live-only without restoring cf-sync cache reads.
    • Update --refresh, README, and cf-hana skill text to describe the retained compatibility flag truthfully.
  11. v0.3.2

    Other 1

    Notes
    • Remove the cf-hana: saved result expires at... stderr notice from query --save output while keeping result refs available for inspection.
  12. v0.3.1

    Other 3

    Notes
    • Add actionable HANA LOB ORDER BY/GROUP BY hints that recommend removing LOB columns or wrapping them with TO_VARCHAR(<column>).
    • Add invalid-column typo suggestions that inspect target table columns and print close matches to stderr.
    • Increase the default saved result TTL from 60 minutes to 7 days.
  13. v0.3.0

    Other 3

    Notes
    • Add invalid table/view recovery suggestions for failed query statements, with nearby table and view names printed to stderr so stdout remains parseable.
    • Add a private local metadata cache under ~/.saptools/cf-hana/metadata with a strict 30-minute TTL and --refresh-metadata bypass.
    • Include schema-scoped SYS.TABLES and SYS.VIEWS metadata for suggestions without caching credentials, parameters, result rows, or table data.
  14. v0.2.0

    Other 5

    Notes
    • Change CLI query output for SELECT/WITH statements to compact CSV and remove --format from query.
    • Default bare SELECT queries to at most 100 returned rows, with accurate N+1 truncation detection.
    • Limit visible SELECT data cells to 128 characters by default, configurable with --cell-limit <n> up to 10,000.
    • Add query --save and cf-hana result commands to save exact returned rows for 60 minutes, inspect rows/cells/JSON paths by ref, search saved values, export exact cells, and prune local result sessions.
    • Keep programmatic query APIs and write backups full-fidelity.
  15. v0.1.6

    Other 3

    Notes
    • Expand fake-backed E2E coverage for complex UPDATE and DELETE backups, including mixed-case keywords, comments, quoted identifiers, nested queries, placeholder filtering, and unscoped writes.
    • Verify backup SELECT failures, write failures, filesystem failures, read-only mode, malformed SQL, and parameter mismatches cannot bypass backup safety.
    • Add opt-in fake-driver statement tracing and deterministic failure injection without recording parameter values.
  16. v0.1.5

    Other 2

    Notes
    • Remove the CLI --no-backup opt-out so cf-hana query always attempts a local backup before running UPDATE or DELETE.
    • Keep backup paths on stderr and keep stdout parseable for table, JSON, and CSV output.
  17. v0.1.4

    Other 3

    Notes
    • Add automatic local CSV backups before CLI query runs an UPDATE or DELETE.
    • Derive the backup SELECT from the write target and top-level WHERE clause, preserving only the WHERE parameters for UPDATE statements.
    • Save each backup in its own non-expiring folder with statement.sql and backup.csv.
  18. v0.1.3

    Other 3

    Notes
    • Add local SQL history for successful direct query and execute calls under ~/.saptools/cf-hana/histories/YYYY-MM-DD.jsonl.
    • Rotate SQL history with five-day retention and keep parameter values, credentials, certificates, and result rows out of the history file.
    • Keep helper-driven catalog SQL out of user SQL history and document the new local state behavior.
  19. v0.1.2

    Other 5

    Notes
    • Harden connection pooling so queued callers continue after transient reconnect failures.
    • Preserve query results when HANA statement cleanup fails and close partially opened clients on schema setup errors.
    • Strengthen read-only and destructive-statement checks around comments, quoted identifiers, and unknown statements.
    • Improve explain() statement isolation, cleanup, and read-only behavior.
    • Validate CLI numeric options strictly and align E2E diagnostics with project defaults.
  20. v0.1.1

    Other 1

    Notes
    • Patch release to publish via npm trusted publishing after the manual 0.1.0 bootstrap.
  21. v0.1.0

    Other 3

    Notes
    • Initial release: run SQL directly against SAP HANA Cloud databases bound to a Cloud Foundry app, addressed by a region/org/space/app selector (or a bare app name).
    • Credentials are resolved cache-first via @saptools/cf-sync, with an on-demand live Cloud Foundry fetch fallback.
    • Includes a HanaClient with pooled connections, parameterized queries, transactions, table introspection, query-builder shorthands, a read-only/destructive-statement safety guard, and a cf-hana CLI.
2downloads per week, npm
Weekly downloads for @saptools/cf-hana26 complete calendar weeks from 2026-04-27 to 2026-10-25; the latest total is 2.27 Apr – 3 May: 04 May – 10 May: 011 May – 17 May: 018 May – 24 May: 20425 May – 31 May: 111 Jun – 7 Jun: 138 Jun – 14 Jun: 1215 Jun – 21 Jun: 1022 Jun – 28 Jun: 1,00429 Jun – 5 Jul: 8606 Jul – 12 Jul: 27213 Jul – 19 Jul: 48920 Jul – 26 Jul: 77127 Jul – 2 Aug: 1003 Aug – 9 Aug: 6410 Aug – 16 Aug: 8517 Aug – 23 Aug: 4624 Aug – 30 Aug: 2831 Aug – 6 Sept: 2097 Sept – 13 Sept: 4414 Sept – 20 Sept: 2221 Sept – 27 Sept: 1528 Sept – 4 Oct: 305 Oct – 11 Oct: 1412 Oct – 18 Oct: 1119 Oct – 25 Oct: 2
Debugging
  1. @saptools/cf-debuggerNode.js inspector tunnel over cf ssh to Cloud Foundry apps
  2. @saptools/cf-explorerFile search in apps deployed to SAP BTP Cloud Foundry over cf ssh
  3. @saptools/cf-function-traceStep-by-step state recorder for one JavaScript function in Node.js
  4. @saptools/cf-inspectorShell debugger for remote Node.js over the Chrome DevTools Protocol
  5. @saptools/cf-live-traceHTTP trace hook streaming a running CF Node.js app's traffic

Details

Sources4
Added by handdiscovered herechecked
npm registry, @saptools/cf-hanaversion, releases, installschecked
github.com/git+https://github.com/dongitran/saptools.gittree, pushes, contributors, CIchecked
packages/cf-hana/README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
deploymentCloud Foundry
latestv0.7.0
first release
releases and cadence24, ~2d median
discovered viaAdded by hand
first seen in the atlas
ecosystem and domainbtp, developer-experience
History in the atlas1
  1. First seen.
For your agent
Ref
artifact:saptools-cf-hana
JSON
/artifacts/saptools-cf-hana.json
MCP
Connecting an agent