@saptools/cf-hana 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/cli.js +85 -26
- package/dist/cli.js.map +1 -1
- package/dist/index.js +85 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- cspell:words VARCHAR -->
|
|
4
4
|
|
|
5
|
+
## 0.3.5 - 2026-07-06
|
|
6
|
+
|
|
7
|
+
- Normalize HANA `BOOLEAN` result columns that arrive from the driver as `1`/`0`
|
|
8
|
+
into JavaScript `true`/`false` values before CLI/API formatting and saved refs.
|
|
9
|
+
|
|
5
10
|
## 0.3.4 - 2026-07-02
|
|
6
11
|
|
|
7
12
|
- 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.
|
package/dist/cli.js
CHANGED
|
@@ -698,10 +698,10 @@ async function listColumns(connection, schema, table) {
|
|
|
698
698
|
|
|
699
699
|
// src/config.ts
|
|
700
700
|
var CLI_NAME = "cf-hana";
|
|
701
|
-
var CLI_VERSION = "0.3.
|
|
701
|
+
var CLI_VERSION = "0.3.5";
|
|
702
702
|
var ENV_PREFIX = "CF_HANA";
|
|
703
|
-
var DEFAULT_QUERY_TIMEOUT_MS =
|
|
704
|
-
var DEFAULT_CONNECT_TIMEOUT_MS =
|
|
703
|
+
var DEFAULT_QUERY_TIMEOUT_MS = 6e4;
|
|
704
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 6e4;
|
|
705
705
|
var DEFAULT_POOL_MAX = 4;
|
|
706
706
|
var DEFAULT_POOL_IDLE_MS = 6e4;
|
|
707
707
|
var DEFAULT_AUTO_LIMIT = 100;
|
|
@@ -738,9 +738,9 @@ import { join as join2 } from "path";
|
|
|
738
738
|
import { promisify } from "util";
|
|
739
739
|
var execFileAsync = promisify(execFile);
|
|
740
740
|
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
741
|
-
var DEFAULT_TIMEOUT_MS =
|
|
741
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
742
742
|
var CF_RETRY_ATTEMPTS = 3;
|
|
743
|
-
var CF_RETRY_BASE_DELAY_MS =
|
|
743
|
+
var CF_RETRY_BASE_DELAY_MS = 500;
|
|
744
744
|
var REGION_API_MAP = {
|
|
745
745
|
ae01: "https://api.cf.ae01.hana.ondemand.com",
|
|
746
746
|
ap01: "https://api.cf.ap01.hana.ondemand.com",
|
|
@@ -862,29 +862,45 @@ function buildEnv(ctx, overrides = {}) {
|
|
|
862
862
|
env["CF_HOME"] = ctx.cfHome;
|
|
863
863
|
return env;
|
|
864
864
|
}
|
|
865
|
-
async function
|
|
866
|
-
const { bin, argsPrefix } = resolveCfBin();
|
|
867
|
-
const env = buildEnv(ctx, overrides);
|
|
865
|
+
async function execWithRetries(bin, args, env, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
868
866
|
let lastErr;
|
|
869
867
|
for (let attempt = 0; attempt < CF_RETRY_ATTEMPTS; attempt++) {
|
|
870
868
|
try {
|
|
871
|
-
const { stdout } = await execFileAsync(bin,
|
|
869
|
+
const { stdout } = await execFileAsync(bin, args, {
|
|
872
870
|
env,
|
|
873
871
|
maxBuffer: MAX_BUFFER,
|
|
874
|
-
timeout:
|
|
872
|
+
timeout: timeoutMs,
|
|
873
|
+
killSignal: "SIGKILL"
|
|
875
874
|
});
|
|
876
875
|
return stdout;
|
|
877
876
|
} catch (err) {
|
|
878
877
|
lastErr = err;
|
|
878
|
+
const e = err;
|
|
879
|
+
const output = `${e.message ?? ""} ${e.stderr ? String(e.stderr) : ""}`.toLowerCase();
|
|
880
|
+
const isTimeout = e.killed === true;
|
|
881
|
+
const isNetworkFlake = output.includes("error performing request") || output.includes("timeout") || output.includes("connection reset") || output.includes("connection refused") || output.includes("eof") || output.includes("502 bad gateway") || output.includes("503 service unavailable") || output.includes("504 gateway timeout") || output.includes("dial tcp") || output.includes("no such host");
|
|
882
|
+
const isEnoent = e.code === "ENOENT";
|
|
883
|
+
if (isEnoent || !isTimeout && !isNetworkFlake) {
|
|
884
|
+
throw err;
|
|
885
|
+
}
|
|
879
886
|
if (attempt < CF_RETRY_ATTEMPTS - 1) {
|
|
880
887
|
await new Promise((r) => setTimeout(r, CF_RETRY_BASE_DELAY_MS * (attempt + 1)));
|
|
881
888
|
}
|
|
882
889
|
}
|
|
883
890
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
891
|
+
throw lastErr;
|
|
892
|
+
}
|
|
893
|
+
async function runCf(args, ctx, overrides = {}) {
|
|
894
|
+
const { bin, argsPrefix } = resolveCfBin();
|
|
895
|
+
const env = buildEnv(ctx, overrides);
|
|
896
|
+
try {
|
|
897
|
+
return await execWithRetries(bin, [...argsPrefix, ...args], env);
|
|
898
|
+
} catch (lastErr) {
|
|
899
|
+
const e = lastErr;
|
|
900
|
+
const detail = e?.stderr ? String(e.stderr) : e?.message ?? "";
|
|
901
|
+
const cmd = args[0] === "auth" ? "cf auth" : `cf ${args.join(" ")}`;
|
|
902
|
+
throw new Error(`${cmd} failed: ${detail}`.trim(), { cause: lastErr });
|
|
903
|
+
}
|
|
888
904
|
}
|
|
889
905
|
async function cfApi(apiEndpoint, ctx) {
|
|
890
906
|
await runCf(["api", apiEndpoint], ctx);
|
|
@@ -906,12 +922,7 @@ async function cfEnvDirect(appName) {
|
|
|
906
922
|
const env = { ...process.env };
|
|
907
923
|
delete env["SAP_EMAIL"];
|
|
908
924
|
delete env["SAP_PASSWORD"];
|
|
909
|
-
|
|
910
|
-
env,
|
|
911
|
-
maxBuffer: MAX_BUFFER,
|
|
912
|
-
timeout: DEFAULT_TIMEOUT_MS
|
|
913
|
-
});
|
|
914
|
-
return stdout;
|
|
925
|
+
return await execWithRetries(bin, [...argsPrefix, "env", appName], env);
|
|
915
926
|
}
|
|
916
927
|
async function readCurrentCfTarget() {
|
|
917
928
|
const { bin, argsPrefix } = resolveCfBin();
|
|
@@ -919,11 +930,7 @@ async function readCurrentCfTarget() {
|
|
|
919
930
|
delete env["SAP_EMAIL"];
|
|
920
931
|
delete env["SAP_PASSWORD"];
|
|
921
932
|
try {
|
|
922
|
-
const
|
|
923
|
-
env,
|
|
924
|
-
maxBuffer: MAX_BUFFER,
|
|
925
|
-
timeout: 1e4
|
|
926
|
-
});
|
|
933
|
+
const stdout = await execWithRetries(bin, [...argsPrefix, "target"], env);
|
|
927
934
|
return parseCfTargetOutput(stdout);
|
|
928
935
|
} catch {
|
|
929
936
|
return void 0;
|
|
@@ -1862,6 +1869,57 @@ function boundedRows(rows, requestedLimit) {
|
|
|
1862
1869
|
truncated: rows.length > requestedLimit
|
|
1863
1870
|
};
|
|
1864
1871
|
}
|
|
1872
|
+
function isBooleanColumn(column) {
|
|
1873
|
+
return column.typeName.trim().toUpperCase() === "BOOLEAN";
|
|
1874
|
+
}
|
|
1875
|
+
function normalizeBooleanCell(value) {
|
|
1876
|
+
if (value === 0) {
|
|
1877
|
+
return false;
|
|
1878
|
+
}
|
|
1879
|
+
if (value === 1) {
|
|
1880
|
+
return true;
|
|
1881
|
+
}
|
|
1882
|
+
if (typeof value !== "string") {
|
|
1883
|
+
return value;
|
|
1884
|
+
}
|
|
1885
|
+
const normalized = value.trim().toLowerCase();
|
|
1886
|
+
if (normalized === "0") {
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
if (normalized === "1") {
|
|
1890
|
+
return true;
|
|
1891
|
+
}
|
|
1892
|
+
if (normalized === "false") {
|
|
1893
|
+
return false;
|
|
1894
|
+
}
|
|
1895
|
+
if (normalized === "true") {
|
|
1896
|
+
return true;
|
|
1897
|
+
}
|
|
1898
|
+
return value;
|
|
1899
|
+
}
|
|
1900
|
+
function normalizeBooleanRow(row, columns) {
|
|
1901
|
+
let normalizedRow;
|
|
1902
|
+
for (const column of columns) {
|
|
1903
|
+
const value = row[column.name];
|
|
1904
|
+
if (value === void 0) {
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
const normalizedValue = normalizeBooleanCell(value);
|
|
1908
|
+
if (normalizedValue === value) {
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
normalizedRow ??= { ...row };
|
|
1912
|
+
normalizedRow[column.name] = normalizedValue;
|
|
1913
|
+
}
|
|
1914
|
+
return normalizedRow ?? row;
|
|
1915
|
+
}
|
|
1916
|
+
function normalizeBooleanRows(rows, columns) {
|
|
1917
|
+
const booleanColumns = columns.filter(isBooleanColumn);
|
|
1918
|
+
if (booleanColumns.length === 0 || rows.length === 0) {
|
|
1919
|
+
return rows;
|
|
1920
|
+
}
|
|
1921
|
+
return rows.map((row) => normalizeBooleanRow(row, booleanColumns));
|
|
1922
|
+
}
|
|
1865
1923
|
async function withTimeout(work, timeoutMs, onTimeout) {
|
|
1866
1924
|
let timer;
|
|
1867
1925
|
const timeout = new Promise((_resolve, reject) => {
|
|
@@ -1947,7 +2005,8 @@ var Connection = class _Connection {
|
|
|
1947
2005
|
}
|
|
1948
2006
|
);
|
|
1949
2007
|
const elapsedMs = Date.now() - started;
|
|
1950
|
-
const
|
|
2008
|
+
const normalizedRows = normalizeBooleanRows(execResult.rows, execResult.columns);
|
|
2009
|
+
const selected = boundedRows(normalizedRows, limited.requestedLimit);
|
|
1951
2010
|
return {
|
|
1952
2011
|
rows: selected.rows,
|
|
1953
2012
|
columns: execResult.columns,
|