@saptools/cf-export 0.1.2 → 0.1.3
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/dist/cli.js +77 -7
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { execFile as execFile2 } from "child_process";
|
|
4
5
|
import process2 from "process";
|
|
6
|
+
import { promisify as promisify2 } from "util";
|
|
5
7
|
import { Command } from "commander";
|
|
8
|
+
import { REGIONS } from "@saptools/cf-sync";
|
|
6
9
|
|
|
7
10
|
// src/types.ts
|
|
8
11
|
var ARTIFACT_NAMES = [
|
|
@@ -368,6 +371,7 @@ function formatExportCompletionMessage(appName, writtenFiles, skipped) {
|
|
|
368
371
|
}
|
|
369
372
|
|
|
370
373
|
// src/cli.ts
|
|
374
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
371
375
|
function requireFlag(value, name) {
|
|
372
376
|
if (value === void 0 || value === "") {
|
|
373
377
|
process2.stderr.write(`Error: --${name} is required
|
|
@@ -376,16 +380,82 @@ function requireFlag(value, name) {
|
|
|
376
380
|
}
|
|
377
381
|
return value;
|
|
378
382
|
}
|
|
379
|
-
function
|
|
383
|
+
async function readCurrentCfTarget() {
|
|
384
|
+
const cfBin = process2.env["CF_EXPORT_CF_BIN"] ?? "cf";
|
|
385
|
+
const isScript = cfBin.endsWith(".mjs") || cfBin.endsWith(".js");
|
|
386
|
+
const file = isScript ? "node" : cfBin;
|
|
387
|
+
const args = isScript ? [cfBin, "target"] : ["target"];
|
|
388
|
+
try {
|
|
389
|
+
const { stdout } = await execFileAsync2(file, args, {
|
|
390
|
+
maxBuffer: 1024 * 1024,
|
|
391
|
+
timeout: 1e4
|
|
392
|
+
});
|
|
393
|
+
return parseCfTargetOutput(stdout);
|
|
394
|
+
} catch {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function parseCfTargetOutput(stdout) {
|
|
399
|
+
const fields = /* @__PURE__ */ new Map();
|
|
400
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
401
|
+
const sep = line.indexOf(":");
|
|
402
|
+
if (sep < 0) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const key = line.slice(0, sep).trim().toLowerCase();
|
|
406
|
+
const value = line.slice(sep + 1).trim();
|
|
407
|
+
if (key && value) {
|
|
408
|
+
fields.set(key, value);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const apiEndpoint = fields.get("api endpoint");
|
|
412
|
+
const orgName = fields.get("org");
|
|
413
|
+
const spaceName = fields.get("space");
|
|
414
|
+
if (!orgName || !spaceName) {
|
|
415
|
+
return void 0;
|
|
416
|
+
}
|
|
417
|
+
let region;
|
|
418
|
+
if (apiEndpoint) {
|
|
419
|
+
const normalized = apiEndpoint.trim().replace(/\/+$/, "").toLowerCase();
|
|
420
|
+
for (const [key, reg] of Object.entries(REGIONS)) {
|
|
421
|
+
const regionObj = reg;
|
|
422
|
+
const regApi = regionObj.apiEndpoint?.trim().replace(/\/+$/, "").toLowerCase();
|
|
423
|
+
if (regApi === normalized) {
|
|
424
|
+
region = key;
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const result = {
|
|
430
|
+
org: orgName,
|
|
431
|
+
space: spaceName
|
|
432
|
+
};
|
|
433
|
+
if (region !== void 0) {
|
|
434
|
+
result.region = region;
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
async function buildTarget(flags) {
|
|
439
|
+
let region = flags.region;
|
|
440
|
+
let org = flags.org;
|
|
441
|
+
let space = flags.space;
|
|
442
|
+
if (!region || !org || !space) {
|
|
443
|
+
const current = await readCurrentCfTarget();
|
|
444
|
+
if (current) {
|
|
445
|
+
region ??= current.region;
|
|
446
|
+
org ??= current.org;
|
|
447
|
+
space ??= current.space;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
380
450
|
return {
|
|
381
|
-
region: requireFlag(
|
|
382
|
-
org: requireFlag(
|
|
383
|
-
space: requireFlag(
|
|
451
|
+
region: requireFlag(region, "region"),
|
|
452
|
+
org: requireFlag(org, "org"),
|
|
453
|
+
space: requireFlag(space, "space"),
|
|
384
454
|
app: requireFlag(flags.app, "app")
|
|
385
455
|
};
|
|
386
456
|
}
|
|
387
457
|
function addTargetOptions(cmd) {
|
|
388
|
-
return cmd.
|
|
458
|
+
return cmd.option("-r, --region <key>", "CF region key (e.g. ap10). Auto-detected from current `cf target` if omitted").option("-o, --org <name>", "CF org name. Auto-detected from current `cf target` if omitted").option("-s, --space <name>", "CF space name. Auto-detected from current `cf target` if omitted").requiredOption("-a, --app <name>", "CF app name");
|
|
389
459
|
}
|
|
390
460
|
function parseArtifactList(files) {
|
|
391
461
|
if (!files || files.length === 0) {
|
|
@@ -408,7 +478,7 @@ function parseArtifactList(files) {
|
|
|
408
478
|
}
|
|
409
479
|
async function main(argv) {
|
|
410
480
|
const program = new Command();
|
|
411
|
-
program.name("
|
|
481
|
+
program.name("cf-export").description(
|
|
412
482
|
"Export project artifacts (package.json, lockfiles, .cdsrc.json, default-env.json, .npmrc) from a running CF app"
|
|
413
483
|
);
|
|
414
484
|
addTargetOptions(
|
|
@@ -419,7 +489,7 @@ async function main(argv) {
|
|
|
419
489
|
(val, prev) => [...prev ?? [], val],
|
|
420
490
|
[]
|
|
421
491
|
).option("--all", "Export all supported artifacts (default behavior)", false).action(async (opts) => {
|
|
422
|
-
const target = buildTarget(opts);
|
|
492
|
+
const target = await buildTarget(opts);
|
|
423
493
|
const outDir = opts.out && opts.out.length > 0 ? opts.out : process2.cwd();
|
|
424
494
|
const remoteRoot = opts.remoteRoot && opts.remoteRoot.trim().length > 0 ? opts.remoteRoot.trim() : void 0;
|
|
425
495
|
const explicitFiles = parseArtifactList(opts.file);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/types.ts","../src/exporter.ts","../src/cf.ts","../src/default-env.ts","../src/remote-paths.ts","../src/session.ts","../src/format.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { Command } from \"commander\";\n\nimport { ARTIFACT_NAMES, exportArtifacts, formatExportCompletionMessage, type ArtifactName, type CfTarget } from \"./index.js\";\n\ninterface TargetFlags {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n}\n\ninterface ExportFlags extends TargetFlags {\n readonly out?: string;\n readonly remoteRoot?: string;\n readonly file?: string[];\n readonly all?: boolean;\n}\n\nfunction requireFlag(value: string | undefined, name: string): string {\n if (value === undefined || value === \"\") {\n process.stderr.write(`Error: --${name} is required\\n`);\n process.exit(1);\n }\n return value;\n}\n\nfunction buildTarget(flags: TargetFlags): CfTarget {\n return {\n region: requireFlag(flags.region, \"region\"),\n org: requireFlag(flags.org, \"org\"),\n space: requireFlag(flags.space, \"space\"),\n app: requireFlag(flags.app, \"app\"),\n };\n}\n\nfunction addTargetOptions(cmd: Command): Command {\n return cmd\n .requiredOption(\"-r, --region <key>\", \"CF region key (e.g. ap10)\")\n .requiredOption(\"-o, --org <name>\", \"CF org name\")\n .requiredOption(\"-s, --space <name>\", \"CF space name\")\n .requiredOption(\"-a, --app <name>\", \"CF app name\");\n}\n\nfunction parseArtifactList(files: string[] | undefined): readonly ArtifactName[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n const result: ArtifactName[] = [];\n for (const chunk of files) {\n const parts = chunk.split(/[,\\s]+/).filter((p) => p.length > 0);\n for (const p of parts) {\n if ((ARTIFACT_NAMES as readonly string[]).includes(p)) {\n result.push(p as ArtifactName);\n } else {\n process.stderr.write(`Error: unknown artifact \"${p}\". Valid: ${ARTIFACT_NAMES.join(\", \")}\\n`);\n process.exit(1);\n }\n }\n }\n return result.length > 0 ? result : undefined;\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"saptools-cf-export\")\n .description(\n \"Export project artifacts (package.json, lockfiles, .cdsrc.json, default-env.json, .npmrc) from a running CF app\",\n );\n\n addTargetOptions(\n program\n .command(\"export\", { isDefault: true })\n .description(\"Export artifacts from the target CF app (default command)\"),\n )\n .option(\"--out <dir>\", \"Output directory (default: current working directory)\")\n .option(\"--remote-root <path>\", \"Hint for the base directory inside the container containing the files\")\n .option(\n \"--file <name>\",\n \"Artifact to export (repeatable). Omit to export all. Example: --file package.json --file pnpm-lock.yaml\",\n (val: string, prev: string[] | undefined) => [...(prev ?? []), val],\n [] as string[],\n )\n .option(\"--all\", \"Export all supported artifacts (default behavior)\", false)\n .action(async (opts: ExportFlags): Promise<void> => {\n const target = buildTarget(opts);\n const outDir = opts.out && opts.out.length > 0 ? opts.out : process.cwd();\n const remoteRoot = opts.remoteRoot && opts.remoteRoot.trim().length > 0 ? opts.remoteRoot.trim() : undefined;\n\n const explicitFiles = parseArtifactList(opts.file);\n const artifacts = opts.all || !explicitFiles ? undefined : explicitFiles;\n\n const result = await exportArtifacts({\n target,\n outDir,\n ...(remoteRoot ? { remoteRoot } : {}),\n ...(artifacts ? { artifacts } : {}),\n });\n\n const msg = formatExportCompletionMessage(target.app, result.writtenFiles, result.skipped);\n process.stdout.write(`${msg}\\n`);\n if (result.skipped.length > 0) {\n process.stdout.write(`Skipped (not found): ${result.skipped.join(\", \")}\\n`);\n }\n });\n\n await program.parseAsync([...argv]);\n}\n\ntry {\n await main(process.argv);\n} catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error: ${msg}\\n`);\n process.exit(1);\n}\n","export interface CfTarget {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport const ARTIFACT_NAMES = [\n \"package.json\",\n \"package-lock.json\",\n \"pnpm-lock.yaml\",\n \".cdsrc.json\",\n \"default-env.json\",\n \".npmrc\",\n] as const;\n\nexport type ArtifactName = (typeof ARTIFACT_NAMES)[number];\n\nexport interface ExportArtifactsOptions {\n readonly target: CfTarget;\n readonly outDir: string;\n readonly remoteRoot?: string;\n /**\n * Subset of artifacts to export. When omitted or empty, all supported artifacts are attempted.\n */\n readonly artifacts?: readonly ArtifactName[];\n}\n\nexport interface ExportArtifactsResult {\n readonly writtenFiles: readonly string[];\n readonly skipped: readonly string[];\n}\n\nexport interface CfExecContext {\n readonly command?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly sensitiveValues?: readonly string[];\n}\n","import { chmod, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport { fetchDefaultEnvJson } from \"./default-env.js\";\nimport { fetchRemoteTextFile } from \"./remote-paths.js\";\nimport { openCfSession } from \"./session.js\";\nimport type { OpenCfSession } from \"./session.js\";\nimport { ARTIFACT_NAMES, type ArtifactName, type CfExecContext, type ExportArtifactsOptions, type ExportArtifactsResult } from \"./types.js\";\n\nfunction resolveAllArtifacts(): readonly ArtifactName[] {\n return [...ARTIFACT_NAMES];\n}\n\nfunction normalizeRequested(requested: readonly ArtifactName[] | undefined): readonly ArtifactName[] {\n if (!requested || requested.length === 0) {\n return resolveAllArtifacts();\n }\n // Deduplicate while preserving order of first occurrence\n const seen = new Set<string>();\n const out: ArtifactName[] = [];\n for (const name of requested) {\n if (ARTIFACT_NAMES.includes(name) && !seen.has(name)) {\n seen.add(name);\n out.push(name);\n }\n }\n return out;\n}\n\nasync function writeArtifact(outDir: string, fileName: string, content: string): Promise<string> {\n const outPath = resolve(join(outDir, fileName));\n await mkdir(dirname(outPath), { recursive: true });\n const isSensitive = fileName === \"default-env.json\" || fileName === \".npmrc\";\n await writeFile(outPath, content, { encoding: \"utf8\" });\n if (isSensitive) {\n await chmod(outPath, 0o600);\n }\n return outPath;\n}\n\nexport async function exportArtifacts(\n options: ExportArtifactsOptions,\n): Promise<ExportArtifactsResult> {\n const requested = options.artifacts;\n if (requested?.length === 0) {\n throw new Error(\"At least one artifact must be selected for export.\");\n }\n const artifacts = normalizeRequested(requested);\n\n const session: OpenCfSession = await openCfSession(options.target);\n const written: string[] = [];\n const skipped: string[] = [];\n\n try {\n for (const name of artifacts) {\n if (name === \"default-env.json\") {\n try {\n const json = await fetchDefaultEnvJson({\n appName: options.target.app,\n context: session.context,\n });\n const path = await writeArtifact(options.outDir, name, json);\n written.push(path);\n } catch {\n // Treat default-env as best-effort too (consistent with \"nếu có\" for all artifacts in default \"all\" mode).\n // Only the presence of the file/VCAP in the remote app determines if it can be exported.\n skipped.push(name);\n }\n continue;\n }\n\n // regular file via ssh\n const remoteRoot = options.remoteRoot;\n const fetchOpts: {\n readonly appName: string;\n readonly fileName: string;\n readonly remoteRoot?: string | undefined;\n readonly context?: CfExecContext;\n } = {\n appName: options.target.app,\n fileName: name,\n context: session.context,\n ...(typeof remoteRoot === \"string\" ? { remoteRoot } : {}),\n };\n const content = await fetchRemoteTextFile(fetchOpts);\n\n if (content === null) {\n skipped.push(name);\n continue;\n }\n\n const path = await writeArtifact(options.outDir, name, content);\n written.push(path);\n }\n } finally {\n await session.dispose();\n }\n\n return {\n writtenFiles: written,\n skipped,\n };\n}\n\nexport function getAllArtifactNames(): readonly ArtifactName[] {\n return resolveAllArtifacts();\n}\n","import { execFile, type ExecFileOptionsWithBufferEncoding } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport type { CfExecContext } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 64 * 1024 * 1024;\n\nconst REMOTE_FILE_SENTINEL = \"__SAPTOOLS_CF_EXPORT_FILE_CONTENT__\";\n\nfunction resolveCfCommand(context?: CfExecContext): string {\n return context?.command ?? process.env[\"CF_EXPORT_CF_BIN\"] ?? \"cf\";\n}\n\nfunction resolveCfEnv(context?: CfExecContext): NodeJS.ProcessEnv {\n const env = context?.env ? { ...process.env, ...context.env } : { ...process.env };\n delete env[\"SAP_EMAIL\"];\n delete env[\"SAP_PASSWORD\"];\n return env;\n}\n\nfunction describeCfCommand(args: readonly string[]): string {\n const [command] = args;\n if (command === undefined) {\n return \"cf\";\n }\n if (command === \"auth\") {\n return \"cf auth\";\n }\n return `cf ${args.join(\" \")}`;\n}\n\nfunction redactSensitiveValue(detail: string, value: string): string {\n if (value.length === 0) {\n return detail;\n }\n return detail.split(value).join(\"[REDACTED]\");\n}\n\nfunction sanitizeCfErrorDetail(\n detail: string,\n args: readonly string[],\n sensitiveValues: readonly string[] = [],\n): string {\n const authArgs = args[0] === \"auth\" ? args.slice(1) : [];\n const values = [...authArgs, ...sensitiveValues];\n return values.reduce((current, value) => redactSensitiveValue(current, value), detail);\n}\n\nfunction errorDetailFrom(err: unknown): string {\n if (typeof err === \"object\" && err !== null) {\n const e = err as { stderr?: Buffer | string; message?: string };\n const detail = e.stderr ?? e.message;\n return Buffer.isBuffer(detail) ? detail.toString(\"utf8\") : (detail ?? \"\");\n }\n return String(err);\n}\n\nfunction withSensitiveEnv(\n context: CfExecContext | undefined,\n env: NodeJS.ProcessEnv,\n sensitiveValues: readonly string[],\n): CfExecContext {\n return {\n ...context,\n env: { ...context?.env, ...env },\n sensitiveValues: [...(context?.sensitiveValues ?? []), ...sensitiveValues],\n };\n}\n\nasync function runCf(args: readonly string[], context?: CfExecContext): Promise<string> {\n const cmd = resolveCfCommand(context);\n const isScript = cmd.endsWith(\".mjs\") || cmd.endsWith(\".js\");\n const file = isScript ? \"node\" : cmd;\n const allArgs = isScript ? [cmd, ...args] : [...args];\n try {\n const { stdout } = await execFileAsync(file, allArgs, {\n env: resolveCfEnv(context),\n maxBuffer: MAX_BUFFER,\n });\n return stdout;\n } catch (err) {\n const command = describeCfCommand(args);\n const detail = sanitizeCfErrorDetail(errorDetailFrom(err), args, context?.sensitiveValues);\n throw new Error(`${command} failed: ${detail}`, { cause: err });\n }\n}\n\nasync function runCfBuffer(\n args: readonly string[],\n context?: CfExecContext,\n): Promise<Buffer> {\n const cmd = resolveCfCommand(context);\n const isScript = cmd.endsWith(\".mjs\") || cmd.endsWith(\".js\");\n const file = isScript ? \"node\" : cmd;\n const allArgs = isScript ? [cmd, ...args] : [...args];\n const options: ExecFileOptionsWithBufferEncoding = {\n env: resolveCfEnv(context),\n maxBuffer: MAX_BUFFER,\n encoding: \"buffer\",\n };\n try {\n const { stdout } = await execFileAsync(file, allArgs, options);\n return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);\n } catch (err) {\n const command = describeCfCommand(args);\n const detail = sanitizeCfErrorDetail(errorDetailFrom(err), args, context?.sensitiveValues);\n throw new Error(`${command} failed: ${detail}`, { cause: err });\n }\n}\n\nexport async function cfApi(apiEndpoint: string, context?: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context?: CfExecContext,\n): Promise<void> {\n const authContext = withSensitiveEnv(\n context,\n {\n CF_USERNAME: email,\n CF_PASSWORD: password,\n },\n [email, password],\n );\n await runCf([\"auth\"], authContext);\n}\n\nexport async function cfTargetSpace(\n org: string,\n space: string,\n context?: CfExecContext,\n): Promise<void> {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n}\n\nexport async function cfAppGuid(appName: string, context?: CfExecContext): Promise<string> {\n const stdout = await runCf([\"app\", appName, \"--guid\"], context);\n const guid = stdout.trim();\n if (guid.length === 0) {\n throw new Error(`CF returned an empty app GUID for \"${appName}\".`);\n }\n return guid;\n}\n\nexport async function cfCurl(path: string, context?: CfExecContext): Promise<string> {\n return await runCf([\"curl\", path], context);\n}\n\nexport async function cfSsh(\n appName: string,\n command: string,\n context?: CfExecContext,\n): Promise<string> {\n return await runCf([\"ssh\", appName, \"--disable-pseudo-tty\", \"-c\", command], context);\n}\n\nexport async function cfSshBuffer(\n appName: string,\n command: string,\n context?: CfExecContext,\n): Promise<Buffer> {\n return await runCfBuffer([\"ssh\", appName, \"--disable-pseudo-tty\", \"-c\", command], context);\n}\n\nexport function buildRemoteFilePaths(fileName: string, remoteRoot: string | undefined): readonly string[] {\n const paths: string[] = [];\n const normalized = remoteRoot?.trim().replace(/\\/+$/, \"\");\n if (normalized !== undefined && normalized.length > 0) {\n paths.push(`${normalized}/${fileName}`);\n }\n const fallbacks = [`/home/vcap/app/${fileName}`, fileName];\n for (const fb of fallbacks) {\n if (!paths.includes(fb)) {\n paths.push(fb);\n }\n }\n return paths;\n}\n\nexport function buildCatCommand(remotePath: string): string {\n const quoted = `'${remotePath.replaceAll(\"'\", \"'\\\\''\")}'`;\n return [\n `if [ -f ${quoted} ]; then`,\n `printf '%s\\\\n' ${quotedForSentinel()};`,\n `cat ${quoted};`,\n \"else exit 66; fi\",\n ].join(\" \");\n}\n\nfunction quotedForSentinel(): string {\n // Sentinel is a constant known only to us; no user content can start with it after trim.\n const s = REMOTE_FILE_SENTINEL;\n return `'${s.replaceAll(\"'\", \"'\\\\''\")}'`;\n}\n\nexport const REMOTE_CONTENT_SENTINEL = REMOTE_FILE_SENTINEL;\n\nexport function parseRemoteFileContent(stdout: string): string | null {\n const prefix = `${REMOTE_FILE_SENTINEL}\\n`;\n if (stdout.startsWith(prefix)) {\n return stdout.slice(prefix.length);\n }\n return null;\n}\n\nexport const internals = {\n runCf,\n describeCfCommand,\n sanitizeCfErrorDetail,\n};\n","import { cfAppGuid, cfCurl } from \"./cf.js\";\nimport type { CfExecContext } from \"./types.js\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction mergeRecordInto(target: Record<string, unknown>, source: unknown): void {\n if (!isRecord(source)) {\n return;\n }\n for (const [k, v] of Object.entries(source)) {\n target[k] = v;\n }\n}\n\nfunction buildDefaultEnvPayload(appEnv: Record<string, unknown>): Record<string, unknown> {\n const payload: Record<string, unknown> = {};\n mergeRecordInto(payload, appEnv[\"system_env_json\"]);\n mergeRecordInto(payload, appEnv[\"environment_variables\"]);\n mergeRecordInto(payload, appEnv[\"running_env_json\"]);\n mergeRecordInto(payload, appEnv[\"staging_env_json\"]);\n\n if (Object.keys(payload).length === 0) {\n throw new Error(\"No environment variables found to build default-env.json.\");\n }\n return payload;\n}\n\nexport interface FetchDefaultEnvOptions {\n readonly appName: string;\n readonly context?: CfExecContext;\n}\n\nexport async function fetchDefaultEnvJson(options: FetchDefaultEnvOptions): Promise<string> {\n const guid = await cfAppGuid(options.appName, options.context);\n const encoded = encodeURIComponent(guid);\n const stdout = await cfCurl(`/v3/apps/${encoded}/env`, options.context);\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stdout);\n } catch {\n throw new Error(\"Unexpected JSON format for CF app environment payload.\");\n }\n\n if (!isRecord(parsed)) {\n throw new Error(\"Unexpected JSON object format for CF app environment payload.\");\n }\n\n const payload = buildDefaultEnvPayload(parsed);\n return `${JSON.stringify(payload, null, 2)}\\n`;\n}\n","import { cfSsh, buildRemoteFilePaths, parseRemoteFileContent, REMOTE_CONTENT_SENTINEL, buildCatCommand } from \"./cf.js\";\nimport type { CfExecContext } from \"./types.js\";\n\nexport interface FetchRemoteTextOptions {\n readonly appName: string;\n readonly fileName: string;\n readonly remoteRoot?: string | undefined;\n readonly context?: CfExecContext;\n}\n\n/**\n * Fetch a text file from the CF app container using cf ssh.\n * Tries remoteRoot (if provided) first, then standard fallbacks.\n * Returns null when the file does not exist in any candidate location.\n */\nexport async function fetchRemoteTextFile(options: FetchRemoteTextOptions): Promise<string | null> {\n const paths = buildRemoteFilePaths(options.fileName, options.remoteRoot);\n\n for (const remotePath of paths) {\n try {\n const cmd = buildCatCommand(remotePath);\n const stdout = await cfSsh(options.appName, cmd, options.context);\n const content = parseRemoteFileContent(stdout);\n if (content !== null) {\n return content;\n }\n } catch {\n // 66 exit or ssh failure for this path → try next\n }\n }\n\n return null;\n}\n\nexport { buildRemoteFilePaths, buildCatCommand, parseRemoteFileContent, REMOTE_CONTENT_SENTINEL };\n","import { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { resolveApiEndpoint, resolveSessionEnv } from \"@saptools/cf-files\";\nimport { cfApi, cfAuth, cfTargetSpace } from \"./cf.js\";\nimport type { CfExecContext, CfTarget } from \"./types.js\";\n\n// Delegate pure resolve helpers to @saptools/cf-files (shared, less duplication for non-exec parts)\nexport { resolveApiEndpoint, resolveSessionEnv } from \"@saptools/cf-files\";\n\n// Local openCfSession still uses our custom CF_EXPORT_* envs and prefix while calling the shared cf* functions.\n\nexport interface SessionEnv {\n readonly email: string;\n readonly password: string;\n}\n\nexport interface OpenCfSession {\n readonly context: CfExecContext;\n readonly dispose: () => Promise<void>;\n}\n\nconst CF_HOME_PREFIX = \"saptools-cf-export-\";\n\n// resolveSessionEnv and resolveApiEndpoint are re-exported from @saptools/cf-files above\n// (keeps implementation in one place, reduces duplication).\n\nfunction explicitCfHome(context?: CfExecContext): string | undefined {\n const fromContext =\n context?.env?.[\"CF_HOME\"] ?? context?.env?.[\"CF_EXPORT_CF_HOME\"];\n if (fromContext !== undefined && fromContext !== \"\") {\n return fromContext;\n }\n const fromProcess = process.env[\"CF_EXPORT_CF_HOME\"];\n return fromProcess === undefined || fromProcess === \"\" ? undefined : fromProcess;\n}\n\nfunction buildSessionEnv(context: CfExecContext | undefined, cfHome: string): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(context?.env ?? {})) {\n if (key !== \"SAP_EMAIL\" && key !== \"SAP_PASSWORD\") {\n env[key] = value;\n }\n }\n env[\"CF_HOME\"] = cfHome;\n return env;\n}\n\nasync function createSessionContext(context?: CfExecContext): Promise<OpenCfSession> {\n const configured = explicitCfHome(context);\n if (configured !== undefined) {\n return {\n context: {\n ...context,\n env: buildSessionEnv(context, configured),\n },\n dispose: (): Promise<void> => Promise.resolve(),\n };\n }\n\n const cfHome = await mkdtemp(join(tmpdir(), CF_HOME_PREFIX));\n return {\n context: {\n ...context,\n env: buildSessionEnv(context, cfHome),\n },\n dispose: async (): Promise<void> => {\n await rm(cfHome, { recursive: true, force: true });\n },\n };\n}\n\nexport async function openCfSession(\n target: CfTarget,\n context?: CfExecContext,\n): Promise<OpenCfSession> {\n const { email, password } = resolveSessionEnv(context?.env);\n const apiEndpoint = resolveApiEndpoint(target.region);\n const session = await createSessionContext(context);\n\n try {\n await cfApi(apiEndpoint, session.context);\n await cfAuth(email, password, session.context);\n await cfTargetSpace(target.org, target.space, session.context);\n return session;\n } catch (err) {\n await session.dispose();\n throw err;\n }\n}\n","\n\nexport function formatExportCompletionMessage(\n appName: string,\n writtenFiles: readonly string[],\n skipped: readonly string[],\n): string {\n const names = writtenFiles.map((p) => {\n const parts = p.split(/[\\\\/]/);\n return parts[parts.length - 1] ?? p;\n });\n\n const base = `Export completed for \"${appName}\".`;\n if (names.length === 0) {\n return `${base} No files written.`;\n }\n\n const filesPart = `${String(names.length)} file${names.length === 1 ? \"\" : \"s\"}: ${names.join(\", \")}`;\n if (skipped.length === 0) {\n return `${base} ${filesPart}.`;\n }\n const skipPart = `Skipped: ${skipped.join(\", \")}`;\n return `${base} ${filesPart}. ${skipPart}.`;\n}\n"],"mappings":"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","names":["process","join","resolveApiEndpoint","resolveSessionEnv","join","path","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/types.ts","../src/exporter.ts","../src/cf.ts","../src/default-env.ts","../src/remote-paths.ts","../src/session.ts","../src/format.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport process from \"node:process\";\nimport { promisify } from \"node:util\";\n\nimport { Command } from \"commander\";\n\nimport { REGIONS } from \"@saptools/cf-sync\";\n\nimport { ARTIFACT_NAMES, exportArtifacts, formatExportCompletionMessage, type ArtifactName, type CfTarget } from \"./index.js\";\n\nconst execFileAsync = promisify(execFile);\n\ninterface TargetFlags {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n}\n\ninterface ExportFlags extends TargetFlags {\n readonly out?: string;\n readonly remoteRoot?: string;\n readonly file?: string[];\n readonly all?: boolean;\n}\n\nfunction requireFlag(value: string | undefined, name: string): string {\n if (value === undefined || value === \"\") {\n process.stderr.write(`Error: --${name} is required\\n`);\n process.exit(1);\n }\n return value;\n}\n\nasync function readCurrentCfTarget(): Promise<{ region?: string; org?: string; space?: string } | undefined> {\n const cfBin = process.env[\"CF_EXPORT_CF_BIN\"] ?? \"cf\";\n const isScript = cfBin.endsWith(\".mjs\") || cfBin.endsWith(\".js\");\n const file = isScript ? \"node\" : cfBin;\n const args = isScript ? [cfBin, \"target\"] : [\"target\"];\n try {\n const { stdout } = await execFileAsync(file, args, {\n maxBuffer: 1024 * 1024,\n timeout: 10000,\n });\n return parseCfTargetOutput(stdout);\n } catch {\n return undefined;\n }\n}\n\nfunction parseCfTargetOutput(stdout: string): { region?: string; org?: string; space?: string } | undefined {\n const fields = new Map<string, string>();\n for (const line of stdout.split(/\\r?\\n/)) {\n const sep = line.indexOf(\":\");\n if (sep < 0) {\n continue;\n }\n const key = line.slice(0, sep).trim().toLowerCase();\n const value = line.slice(sep + 1).trim();\n if (key && value) {\n fields.set(key, value);\n }\n }\n\n const apiEndpoint = fields.get(\"api endpoint\");\n const orgName = fields.get(\"org\");\n const spaceName = fields.get(\"space\");\n\n if (!orgName || !spaceName) {\n return undefined;\n }\n\n let region: string | undefined;\n if (apiEndpoint) {\n const normalized = apiEndpoint.trim().replace(/\\/+$/, \"\").toLowerCase();\n for (const [key, reg] of Object.entries(REGIONS)) {\n const regionObj = reg as { apiEndpoint?: string };\n const regApi = regionObj.apiEndpoint?.trim().replace(/\\/+$/, \"\").toLowerCase();\n if (regApi === normalized) {\n region = key;\n break;\n }\n }\n }\n\n const result: { region?: string; org?: string; space?: string } = {\n org: orgName,\n space: spaceName,\n };\n if (region !== undefined) {\n result.region = region;\n }\n return result;\n}\n\nasync function buildTarget(flags: TargetFlags): Promise<CfTarget> {\n let region = flags.region;\n let org = flags.org;\n let space = flags.space;\n\n if (!region || !org || !space) {\n const current = await readCurrentCfTarget();\n if (current) {\n region ??= current.region;\n org ??= current.org;\n space ??= current.space;\n }\n }\n\n return {\n region: requireFlag(region, \"region\"),\n org: requireFlag(org, \"org\"),\n space: requireFlag(space, \"space\"),\n app: requireFlag(flags.app, \"app\"),\n };\n}\n\nfunction addTargetOptions(cmd: Command): Command {\n return cmd\n .option(\"-r, --region <key>\", \"CF region key (e.g. ap10). Auto-detected from current `cf target` if omitted\")\n .option(\"-o, --org <name>\", \"CF org name. Auto-detected from current `cf target` if omitted\")\n .option(\"-s, --space <name>\", \"CF space name. Auto-detected from current `cf target` if omitted\")\n .requiredOption(\"-a, --app <name>\", \"CF app name\");\n}\n\nfunction parseArtifactList(files: string[] | undefined): readonly ArtifactName[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n const result: ArtifactName[] = [];\n for (const chunk of files) {\n const parts = chunk.split(/[,\\s]+/).filter((p) => p.length > 0);\n for (const p of parts) {\n if ((ARTIFACT_NAMES as readonly string[]).includes(p)) {\n result.push(p as ArtifactName);\n } else {\n process.stderr.write(`Error: unknown artifact \"${p}\". Valid: ${ARTIFACT_NAMES.join(\", \")}\\n`);\n process.exit(1);\n }\n }\n }\n return result.length > 0 ? result : undefined;\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"cf-export\")\n .description(\n \"Export project artifacts (package.json, lockfiles, .cdsrc.json, default-env.json, .npmrc) from a running CF app\",\n );\n\n addTargetOptions(\n program\n .command(\"export\", { isDefault: true })\n .description(\"Export artifacts from the target CF app (default command)\"),\n )\n .option(\"--out <dir>\", \"Output directory (default: current working directory)\")\n .option(\"--remote-root <path>\", \"Hint for the base directory inside the container containing the files\")\n .option(\n \"--file <name>\",\n \"Artifact to export (repeatable). Omit to export all. Example: --file package.json --file pnpm-lock.yaml\",\n (val: string, prev: string[] | undefined) => [...(prev ?? []), val],\n [] as string[],\n )\n .option(\"--all\", \"Export all supported artifacts (default behavior)\", false)\n .action(async (opts: ExportFlags): Promise<void> => {\n const target = await buildTarget(opts);\n const outDir = opts.out && opts.out.length > 0 ? opts.out : process.cwd();\n const remoteRoot = opts.remoteRoot && opts.remoteRoot.trim().length > 0 ? opts.remoteRoot.trim() : undefined;\n\n const explicitFiles = parseArtifactList(opts.file);\n const artifacts = opts.all || !explicitFiles ? undefined : explicitFiles;\n\n const result = await exportArtifacts({\n target,\n outDir,\n ...(remoteRoot ? { remoteRoot } : {}),\n ...(artifacts ? { artifacts } : {}),\n });\n\n const msg = formatExportCompletionMessage(target.app, result.writtenFiles, result.skipped);\n process.stdout.write(`${msg}\\n`);\n if (result.skipped.length > 0) {\n process.stdout.write(`Skipped (not found): ${result.skipped.join(\", \")}\\n`);\n }\n });\n\n await program.parseAsync([...argv]);\n}\n\ntry {\n await main(process.argv);\n} catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error: ${msg}\\n`);\n process.exit(1);\n}\n","export interface CfTarget {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport const ARTIFACT_NAMES = [\n \"package.json\",\n \"package-lock.json\",\n \"pnpm-lock.yaml\",\n \".cdsrc.json\",\n \"default-env.json\",\n \".npmrc\",\n] as const;\n\nexport type ArtifactName = (typeof ARTIFACT_NAMES)[number];\n\nexport interface ExportArtifactsOptions {\n readonly target: CfTarget;\n readonly outDir: string;\n readonly remoteRoot?: string;\n /**\n * Subset of artifacts to export. When omitted or empty, all supported artifacts are attempted.\n */\n readonly artifacts?: readonly ArtifactName[];\n}\n\nexport interface ExportArtifactsResult {\n readonly writtenFiles: readonly string[];\n readonly skipped: readonly string[];\n}\n\nexport interface CfExecContext {\n readonly command?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly sensitiveValues?: readonly string[];\n}\n","import { chmod, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport { fetchDefaultEnvJson } from \"./default-env.js\";\nimport { fetchRemoteTextFile } from \"./remote-paths.js\";\nimport { openCfSession } from \"./session.js\";\nimport type { OpenCfSession } from \"./session.js\";\nimport { ARTIFACT_NAMES, type ArtifactName, type CfExecContext, type ExportArtifactsOptions, type ExportArtifactsResult } from \"./types.js\";\n\nfunction resolveAllArtifacts(): readonly ArtifactName[] {\n return [...ARTIFACT_NAMES];\n}\n\nfunction normalizeRequested(requested: readonly ArtifactName[] | undefined): readonly ArtifactName[] {\n if (!requested || requested.length === 0) {\n return resolveAllArtifacts();\n }\n // Deduplicate while preserving order of first occurrence\n const seen = new Set<string>();\n const out: ArtifactName[] = [];\n for (const name of requested) {\n if (ARTIFACT_NAMES.includes(name) && !seen.has(name)) {\n seen.add(name);\n out.push(name);\n }\n }\n return out;\n}\n\nasync function writeArtifact(outDir: string, fileName: string, content: string): Promise<string> {\n const outPath = resolve(join(outDir, fileName));\n await mkdir(dirname(outPath), { recursive: true });\n const isSensitive = fileName === \"default-env.json\" || fileName === \".npmrc\";\n await writeFile(outPath, content, { encoding: \"utf8\" });\n if (isSensitive) {\n await chmod(outPath, 0o600);\n }\n return outPath;\n}\n\nexport async function exportArtifacts(\n options: ExportArtifactsOptions,\n): Promise<ExportArtifactsResult> {\n const requested = options.artifacts;\n if (requested?.length === 0) {\n throw new Error(\"At least one artifact must be selected for export.\");\n }\n const artifacts = normalizeRequested(requested);\n\n const session: OpenCfSession = await openCfSession(options.target);\n const written: string[] = [];\n const skipped: string[] = [];\n\n try {\n for (const name of artifacts) {\n if (name === \"default-env.json\") {\n try {\n const json = await fetchDefaultEnvJson({\n appName: options.target.app,\n context: session.context,\n });\n const path = await writeArtifact(options.outDir, name, json);\n written.push(path);\n } catch {\n // Treat default-env as best-effort too (consistent with \"nếu có\" for all artifacts in default \"all\" mode).\n // Only the presence of the file/VCAP in the remote app determines if it can be exported.\n skipped.push(name);\n }\n continue;\n }\n\n // regular file via ssh\n const remoteRoot = options.remoteRoot;\n const fetchOpts: {\n readonly appName: string;\n readonly fileName: string;\n readonly remoteRoot?: string | undefined;\n readonly context?: CfExecContext;\n } = {\n appName: options.target.app,\n fileName: name,\n context: session.context,\n ...(typeof remoteRoot === \"string\" ? { remoteRoot } : {}),\n };\n const content = await fetchRemoteTextFile(fetchOpts);\n\n if (content === null) {\n skipped.push(name);\n continue;\n }\n\n const path = await writeArtifact(options.outDir, name, content);\n written.push(path);\n }\n } finally {\n await session.dispose();\n }\n\n return {\n writtenFiles: written,\n skipped,\n };\n}\n\nexport function getAllArtifactNames(): readonly ArtifactName[] {\n return resolveAllArtifacts();\n}\n","import { execFile, type ExecFileOptionsWithBufferEncoding } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport type { CfExecContext } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 64 * 1024 * 1024;\n\nconst REMOTE_FILE_SENTINEL = \"__SAPTOOLS_CF_EXPORT_FILE_CONTENT__\";\n\nfunction resolveCfCommand(context?: CfExecContext): string {\n return context?.command ?? process.env[\"CF_EXPORT_CF_BIN\"] ?? \"cf\";\n}\n\nfunction resolveCfEnv(context?: CfExecContext): NodeJS.ProcessEnv {\n const env = context?.env ? { ...process.env, ...context.env } : { ...process.env };\n delete env[\"SAP_EMAIL\"];\n delete env[\"SAP_PASSWORD\"];\n return env;\n}\n\nfunction describeCfCommand(args: readonly string[]): string {\n const [command] = args;\n if (command === undefined) {\n return \"cf\";\n }\n if (command === \"auth\") {\n return \"cf auth\";\n }\n return `cf ${args.join(\" \")}`;\n}\n\nfunction redactSensitiveValue(detail: string, value: string): string {\n if (value.length === 0) {\n return detail;\n }\n return detail.split(value).join(\"[REDACTED]\");\n}\n\nfunction sanitizeCfErrorDetail(\n detail: string,\n args: readonly string[],\n sensitiveValues: readonly string[] = [],\n): string {\n const authArgs = args[0] === \"auth\" ? args.slice(1) : [];\n const values = [...authArgs, ...sensitiveValues];\n return values.reduce((current, value) => redactSensitiveValue(current, value), detail);\n}\n\nfunction errorDetailFrom(err: unknown): string {\n if (typeof err === \"object\" && err !== null) {\n const e = err as { stderr?: Buffer | string; message?: string };\n const detail = e.stderr ?? e.message;\n return Buffer.isBuffer(detail) ? detail.toString(\"utf8\") : (detail ?? \"\");\n }\n return String(err);\n}\n\nfunction withSensitiveEnv(\n context: CfExecContext | undefined,\n env: NodeJS.ProcessEnv,\n sensitiveValues: readonly string[],\n): CfExecContext {\n return {\n ...context,\n env: { ...context?.env, ...env },\n sensitiveValues: [...(context?.sensitiveValues ?? []), ...sensitiveValues],\n };\n}\n\nasync function runCf(args: readonly string[], context?: CfExecContext): Promise<string> {\n const cmd = resolveCfCommand(context);\n const isScript = cmd.endsWith(\".mjs\") || cmd.endsWith(\".js\");\n const file = isScript ? \"node\" : cmd;\n const allArgs = isScript ? [cmd, ...args] : [...args];\n try {\n const { stdout } = await execFileAsync(file, allArgs, {\n env: resolveCfEnv(context),\n maxBuffer: MAX_BUFFER,\n });\n return stdout;\n } catch (err) {\n const command = describeCfCommand(args);\n const detail = sanitizeCfErrorDetail(errorDetailFrom(err), args, context?.sensitiveValues);\n throw new Error(`${command} failed: ${detail}`, { cause: err });\n }\n}\n\nasync function runCfBuffer(\n args: readonly string[],\n context?: CfExecContext,\n): Promise<Buffer> {\n const cmd = resolveCfCommand(context);\n const isScript = cmd.endsWith(\".mjs\") || cmd.endsWith(\".js\");\n const file = isScript ? \"node\" : cmd;\n const allArgs = isScript ? [cmd, ...args] : [...args];\n const options: ExecFileOptionsWithBufferEncoding = {\n env: resolveCfEnv(context),\n maxBuffer: MAX_BUFFER,\n encoding: \"buffer\",\n };\n try {\n const { stdout } = await execFileAsync(file, allArgs, options);\n return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);\n } catch (err) {\n const command = describeCfCommand(args);\n const detail = sanitizeCfErrorDetail(errorDetailFrom(err), args, context?.sensitiveValues);\n throw new Error(`${command} failed: ${detail}`, { cause: err });\n }\n}\n\nexport async function cfApi(apiEndpoint: string, context?: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context?: CfExecContext,\n): Promise<void> {\n const authContext = withSensitiveEnv(\n context,\n {\n CF_USERNAME: email,\n CF_PASSWORD: password,\n },\n [email, password],\n );\n await runCf([\"auth\"], authContext);\n}\n\nexport async function cfTargetSpace(\n org: string,\n space: string,\n context?: CfExecContext,\n): Promise<void> {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n}\n\nexport async function cfAppGuid(appName: string, context?: CfExecContext): Promise<string> {\n const stdout = await runCf([\"app\", appName, \"--guid\"], context);\n const guid = stdout.trim();\n if (guid.length === 0) {\n throw new Error(`CF returned an empty app GUID for \"${appName}\".`);\n }\n return guid;\n}\n\nexport async function cfCurl(path: string, context?: CfExecContext): Promise<string> {\n return await runCf([\"curl\", path], context);\n}\n\nexport async function cfSsh(\n appName: string,\n command: string,\n context?: CfExecContext,\n): Promise<string> {\n return await runCf([\"ssh\", appName, \"--disable-pseudo-tty\", \"-c\", command], context);\n}\n\nexport async function cfSshBuffer(\n appName: string,\n command: string,\n context?: CfExecContext,\n): Promise<Buffer> {\n return await runCfBuffer([\"ssh\", appName, \"--disable-pseudo-tty\", \"-c\", command], context);\n}\n\nexport function buildRemoteFilePaths(fileName: string, remoteRoot: string | undefined): readonly string[] {\n const paths: string[] = [];\n const normalized = remoteRoot?.trim().replace(/\\/+$/, \"\");\n if (normalized !== undefined && normalized.length > 0) {\n paths.push(`${normalized}/${fileName}`);\n }\n const fallbacks = [`/home/vcap/app/${fileName}`, fileName];\n for (const fb of fallbacks) {\n if (!paths.includes(fb)) {\n paths.push(fb);\n }\n }\n return paths;\n}\n\nexport function buildCatCommand(remotePath: string): string {\n const quoted = `'${remotePath.replaceAll(\"'\", \"'\\\\''\")}'`;\n return [\n `if [ -f ${quoted} ]; then`,\n `printf '%s\\\\n' ${quotedForSentinel()};`,\n `cat ${quoted};`,\n \"else exit 66; fi\",\n ].join(\" \");\n}\n\nfunction quotedForSentinel(): string {\n // Sentinel is a constant known only to us; no user content can start with it after trim.\n const s = REMOTE_FILE_SENTINEL;\n return `'${s.replaceAll(\"'\", \"'\\\\''\")}'`;\n}\n\nexport const REMOTE_CONTENT_SENTINEL = REMOTE_FILE_SENTINEL;\n\nexport function parseRemoteFileContent(stdout: string): string | null {\n const prefix = `${REMOTE_FILE_SENTINEL}\\n`;\n if (stdout.startsWith(prefix)) {\n return stdout.slice(prefix.length);\n }\n return null;\n}\n\nexport const internals = {\n runCf,\n describeCfCommand,\n sanitizeCfErrorDetail,\n};\n","import { cfAppGuid, cfCurl } from \"./cf.js\";\nimport type { CfExecContext } from \"./types.js\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction mergeRecordInto(target: Record<string, unknown>, source: unknown): void {\n if (!isRecord(source)) {\n return;\n }\n for (const [k, v] of Object.entries(source)) {\n target[k] = v;\n }\n}\n\nfunction buildDefaultEnvPayload(appEnv: Record<string, unknown>): Record<string, unknown> {\n const payload: Record<string, unknown> = {};\n mergeRecordInto(payload, appEnv[\"system_env_json\"]);\n mergeRecordInto(payload, appEnv[\"environment_variables\"]);\n mergeRecordInto(payload, appEnv[\"running_env_json\"]);\n mergeRecordInto(payload, appEnv[\"staging_env_json\"]);\n\n if (Object.keys(payload).length === 0) {\n throw new Error(\"No environment variables found to build default-env.json.\");\n }\n return payload;\n}\n\nexport interface FetchDefaultEnvOptions {\n readonly appName: string;\n readonly context?: CfExecContext;\n}\n\nexport async function fetchDefaultEnvJson(options: FetchDefaultEnvOptions): Promise<string> {\n const guid = await cfAppGuid(options.appName, options.context);\n const encoded = encodeURIComponent(guid);\n const stdout = await cfCurl(`/v3/apps/${encoded}/env`, options.context);\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stdout);\n } catch {\n throw new Error(\"Unexpected JSON format for CF app environment payload.\");\n }\n\n if (!isRecord(parsed)) {\n throw new Error(\"Unexpected JSON object format for CF app environment payload.\");\n }\n\n const payload = buildDefaultEnvPayload(parsed);\n return `${JSON.stringify(payload, null, 2)}\\n`;\n}\n","import { cfSsh, buildRemoteFilePaths, parseRemoteFileContent, REMOTE_CONTENT_SENTINEL, buildCatCommand } from \"./cf.js\";\nimport type { CfExecContext } from \"./types.js\";\n\nexport interface FetchRemoteTextOptions {\n readonly appName: string;\n readonly fileName: string;\n readonly remoteRoot?: string | undefined;\n readonly context?: CfExecContext;\n}\n\n/**\n * Fetch a text file from the CF app container using cf ssh.\n * Tries remoteRoot (if provided) first, then standard fallbacks.\n * Returns null when the file does not exist in any candidate location.\n */\nexport async function fetchRemoteTextFile(options: FetchRemoteTextOptions): Promise<string | null> {\n const paths = buildRemoteFilePaths(options.fileName, options.remoteRoot);\n\n for (const remotePath of paths) {\n try {\n const cmd = buildCatCommand(remotePath);\n const stdout = await cfSsh(options.appName, cmd, options.context);\n const content = parseRemoteFileContent(stdout);\n if (content !== null) {\n return content;\n }\n } catch {\n // 66 exit or ssh failure for this path → try next\n }\n }\n\n return null;\n}\n\nexport { buildRemoteFilePaths, buildCatCommand, parseRemoteFileContent, REMOTE_CONTENT_SENTINEL };\n","import { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { resolveApiEndpoint, resolveSessionEnv } from \"@saptools/cf-files\";\nimport { cfApi, cfAuth, cfTargetSpace } from \"./cf.js\";\nimport type { CfExecContext, CfTarget } from \"./types.js\";\n\n// Delegate pure resolve helpers to @saptools/cf-files (shared, less duplication for non-exec parts)\nexport { resolveApiEndpoint, resolveSessionEnv } from \"@saptools/cf-files\";\n\n// Local openCfSession still uses our custom CF_EXPORT_* envs and prefix while calling the shared cf* functions.\n\nexport interface SessionEnv {\n readonly email: string;\n readonly password: string;\n}\n\nexport interface OpenCfSession {\n readonly context: CfExecContext;\n readonly dispose: () => Promise<void>;\n}\n\nconst CF_HOME_PREFIX = \"saptools-cf-export-\";\n\n// resolveSessionEnv and resolveApiEndpoint are re-exported from @saptools/cf-files above\n// (keeps implementation in one place, reduces duplication).\n\nfunction explicitCfHome(context?: CfExecContext): string | undefined {\n const fromContext =\n context?.env?.[\"CF_HOME\"] ?? context?.env?.[\"CF_EXPORT_CF_HOME\"];\n if (fromContext !== undefined && fromContext !== \"\") {\n return fromContext;\n }\n const fromProcess = process.env[\"CF_EXPORT_CF_HOME\"];\n return fromProcess === undefined || fromProcess === \"\" ? undefined : fromProcess;\n}\n\nfunction buildSessionEnv(context: CfExecContext | undefined, cfHome: string): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(context?.env ?? {})) {\n if (key !== \"SAP_EMAIL\" && key !== \"SAP_PASSWORD\") {\n env[key] = value;\n }\n }\n env[\"CF_HOME\"] = cfHome;\n return env;\n}\n\nasync function createSessionContext(context?: CfExecContext): Promise<OpenCfSession> {\n const configured = explicitCfHome(context);\n if (configured !== undefined) {\n return {\n context: {\n ...context,\n env: buildSessionEnv(context, configured),\n },\n dispose: (): Promise<void> => Promise.resolve(),\n };\n }\n\n const cfHome = await mkdtemp(join(tmpdir(), CF_HOME_PREFIX));\n return {\n context: {\n ...context,\n env: buildSessionEnv(context, cfHome),\n },\n dispose: async (): Promise<void> => {\n await rm(cfHome, { recursive: true, force: true });\n },\n };\n}\n\nexport async function openCfSession(\n target: CfTarget,\n context?: CfExecContext,\n): Promise<OpenCfSession> {\n const { email, password } = resolveSessionEnv(context?.env);\n const apiEndpoint = resolveApiEndpoint(target.region);\n const session = await createSessionContext(context);\n\n try {\n await cfApi(apiEndpoint, session.context);\n await cfAuth(email, password, session.context);\n await cfTargetSpace(target.org, target.space, session.context);\n return session;\n } catch (err) {\n await session.dispose();\n throw err;\n }\n}\n","\n\nexport function formatExportCompletionMessage(\n appName: string,\n writtenFiles: readonly string[],\n skipped: readonly string[],\n): string {\n const names = writtenFiles.map((p) => {\n const parts = p.split(/[\\\\/]/);\n return parts[parts.length - 1] ?? p;\n });\n\n const base = `Export completed for \"${appName}\".`;\n if (names.length === 0) {\n return `${base} No files written.`;\n }\n\n const filesPart = `${String(names.length)} file${names.length === 1 ? \"\" : \"s\"}: ${names.join(\", \")}`;\n if (skipped.length === 0) {\n return `${base} ${filesPart}.`;\n }\n const skipPart = `Skipped: ${skipped.join(\", \")}`;\n return `${base} ${filesPart}. ${skipPart}.`;\n}\n"],"mappings":"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","names":["execFile","process","promisify","join","resolveApiEndpoint","resolveSessionEnv","join","path","execFileAsync","promisify","execFile","process"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saptools/cf-export",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Export CAP/CF project artifacts (package.json, lockfiles, .cdsrc.json, default-env.json, .npmrc) from running SAP BTP Cloud Foundry apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|