@saptools/bruno 0.2.6 โ†’ 0.2.7

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/README.md CHANGED
@@ -46,7 +46,7 @@ You just ran Bruno against a production-grade XSUAA-protected service **without
46
46
  - ๐Ÿ“ฆ **Bundled Bruno CLI fallback** โ€” if `bru` is already on your `PATH`, `saptools-bruno` uses it. If not, it falls back to the bundled [`@usebruno/cli`](https://www.npmjs.com/package/@usebruno/cli).
47
47
  - ๐ŸŽฏ **Default context** โ€” `saptools-bruno use <shorthand>` pins a target so subsequent `run` calls need zero arguments. Feels like `cf target` for Bruno.
48
48
  - ๐Ÿงฉ **CLI & typed API** โ€” every command has a zero-config Node.js equivalent. Full TypeScript definitions shipped. Bring your own prompts for headless/CI use.
49
- - ๐Ÿงช **Fully tested** โ€” 81 unit tests + 7 offline e2e tests (stub `bru` binary + fixture CF snapshot). No network required in CI.
49
+ - ๐Ÿงช **Fully tested** โ€” 83 unit tests + 7 offline e2e tests (stub `bru` binary + fixture CF snapshot). No network required in CI.
50
50
  - ๐Ÿชถ **Small + boring** โ€” three runtime deps, no background daemons, no plugin system, no magic.
51
51
 
52
52
  ---
@@ -129,6 +129,7 @@ After `setup-app`, your workspace looks like this:
129
129
 
130
130
  ```text
131
131
  .
132
+ โ”œโ”€โ”€ bruno.json
132
133
  โ””โ”€โ”€ region__ap10/
133
134
  โ””โ”€โ”€ org__my-org/
134
135
  โ””โ”€โ”€ space__dev/
@@ -171,6 +172,7 @@ saptools-bruno --collection ./collections setup-app
171
172
 
172
173
  **What you get**
173
174
 
175
+ - A root-level `bruno.json` if the collection directory does not already have one
174
176
  - Folder tree: `region__<key>/org__<org>/space__<space>/<app>/environments/`
175
177
  - One `.bru` env file per selection, each seeded with `__cf_region`, `__cf_org`, `__cf_space`, `__cf_app`, `environment`, and an empty `baseUrl`
176
178
  - Existing env files are preserved; only missing `__cf_*` vars are patched back in
@@ -304,6 +306,7 @@ All state lives under your home directory or your collection root:
304
306
  ~/.saptools/bruno-context.json # pinned region/org/space/app + updatedAt
305
307
 
306
308
  <root>/
309
+ โ”œโ”€โ”€ bruno.json
307
310
  โ””โ”€โ”€ region__<key>/
308
311
  โ””โ”€โ”€ org__<org>/
309
312
  โ””โ”€โ”€ space__<space>/
package/dist/cli.js CHANGED
@@ -66,7 +66,7 @@ import { Separator, checkbox, input } from "@inquirer/prompts";
66
66
 
67
67
  // src/setup-app.ts
68
68
  import { mkdir as mkdir2, readdir, writeFile as writeFile3 } from "fs/promises";
69
- import { join as join2 } from "path";
69
+ import { basename, join as join2 } from "path";
70
70
 
71
71
  // src/cf-info.ts
72
72
  import {
@@ -317,6 +317,7 @@ async function writeCfMetaToFile(path, ref, baseUrl) {
317
317
 
318
318
  // src/setup-app.ts
319
319
  var COMMON_ENVIRONMENTS = ["local", "dev", "staging", "prod"];
320
+ var BRUNO_COLLECTION_CONFIG_FILENAME = "bruno.json";
320
321
  var ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
321
322
  function assertValidEnvName(name) {
322
323
  if (!ENV_NAME_PATTERN.test(name)) {
@@ -339,6 +340,33 @@ function emptyEnvContent(envName, ref) {
339
340
  ];
340
341
  return lines.join("\n");
341
342
  }
343
+ function normalizeCollectionName(root) {
344
+ const candidate = basename(root).replace(/^\.+/, "").trim();
345
+ return candidate.length > 0 ? candidate : "bruno-collection";
346
+ }
347
+ function defaultBrunoConfig(root) {
348
+ return `${JSON.stringify(
349
+ {
350
+ version: "1",
351
+ name: normalizeCollectionName(root),
352
+ type: "collection",
353
+ ignore: ["node_modules", ".git"]
354
+ },
355
+ null,
356
+ 2
357
+ )}
358
+ `;
359
+ }
360
+ async function ensureCollectionConfig(root) {
361
+ const filePath = join2(root, BRUNO_COLLECTION_CONFIG_FILENAME);
362
+ try {
363
+ await writeFile3(filePath, defaultBrunoConfig(root), { encoding: "utf8", flag: "wx" });
364
+ } catch (err) {
365
+ if (err.code !== "EEXIST") {
366
+ throw err;
367
+ }
368
+ }
369
+ }
342
370
  async function ensureEnvFile(appPath, envName, ref) {
343
371
  const envDir = join2(appPath, ENVIRONMENTS_DIR);
344
372
  await mkdir2(envDir, { recursive: true });
@@ -412,6 +440,8 @@ async function setupApp(options) {
412
440
  if (!confirmed) {
413
441
  return { ref, appPath, environments: [], created: false };
414
442
  }
443
+ await mkdir2(options.root, { recursive: true });
444
+ await ensureCollectionConfig(options.root);
415
445
  await mkdir2(appPath, { recursive: true });
416
446
  const existingEnvs = await listExistingEnvs(appPath);
417
447
  const common = [...COMMON_ENVIRONMENTS];
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/context.ts","../src/paths.ts","../src/environment-prompt.ts","../src/setup-app.ts","../src/cf-info.ts","../src/cf-meta.ts","../src/bru-parser.ts","../src/bru-writer.ts","../src/run.ts","../src/folder-scan.ts","../src/use.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { confirm, select } from \"@inquirer/prompts\";\nimport { Command, Option } from \"commander\";\n\nimport { readContext } from \"./context.js\";\nimport { promptForEnvironments } from \"./environment-prompt.js\";\nimport { runBruno } from \"./run.js\";\nimport { setupApp } from \"./setup-app.js\";\nimport { useContext } from \"./use.js\";\n\nfunction resolveCollectionDir(explicitCollection: string | undefined, explicitRoot: string | undefined): string {\n if (explicitCollection) {\n return explicitCollection;\n }\n if (explicitRoot) {\n return explicitRoot;\n }\n if (process.env[\"SAPTOOLS_BRUNO_COLLECTION\"]) {\n return process.env[\"SAPTOOLS_BRUNO_COLLECTION\"];\n }\n if (process.env[\"SAPTOOLS_BRUNO_ROOT\"]) {\n return process.env[\"SAPTOOLS_BRUNO_ROOT\"];\n }\n return process.cwd();\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"saptools-bruno\")\n .description(\"Smart runner for Bruno with CF-aware env metadata and automatic token injection\")\n .addOption(new Option(\"--collection <dir>\", \"Bruno collection directory (default: SAPTOOLS_BRUNO_COLLECTION or cwd)\"))\n .addOption(new Option(\"--root <dir>\", \"Legacy alias for --collection\").hideHelp());\n\n program\n .command(\"setup-app\")\n .description(\"Interactively scaffold a bruno app folder and seed __cf_* variables\")\n .action(async (): Promise<void> => {\n const collectionDir = resolveCollectionDir(\n program.opts<{ collection?: string; root?: string }>().collection,\n program.opts<{ collection?: string; root?: string }>().root,\n );\n const result = await setupApp({\n root: collectionDir,\n prompts: {\n selectRegion: async (choices) => await select({ message: \"Select region\", choices: [...choices] }),\n selectOrg: async (choices) => await select({ message: \"Select org\", choices: [...choices] }),\n selectSpace: async (choices) => await select({ message: \"Select space\", choices: [...choices] }),\n selectApp: async (choices) => await select({ message: \"Select app\", choices: [...choices] }),\n confirmCreate: async (path) => await confirm({ message: `Create ${path}?`, default: true }),\n selectEnvironments: async (opts) => await promptForEnvironments(opts),\n },\n log: (msg) => {\n process.stdout.write(`${msg}\\n`);\n },\n });\n if (!result.created) {\n process.stdout.write(\"Aborted.\\n\");\n return;\n }\n process.stdout.write(`โœ” App folder ready at ${result.appPath}\\n`);\n });\n\n program\n .command(\"run\")\n .description(\"Run a bruno request or folder, auto-injecting an XSUAA token\")\n .argument(\"[target]\", \"Shorthand path (region/org/space/app[/folder/file.bru]) or real path\")\n .option(\"-e, --env <name>\", \"Environment name (default: context or first)\")\n .action(\n async (\n target: string | undefined,\n opts: { env?: string },\n ): Promise<void> => {\n const collectionDir = resolveCollectionDir(\n program.opts<{ collection?: string; root?: string }>().collection,\n program.opts<{ collection?: string; root?: string }>().root,\n );\n let effectiveTarget = target;\n\n if (!effectiveTarget) {\n const ctx = await readContext();\n if (!ctx) {\n throw new Error(\n \"No target specified and no default context is set. Run `saptools-bruno use <region/org/space/app>` first.\",\n );\n }\n effectiveTarget = `${ctx.region}/${ctx.org}/${ctx.space}/${ctx.app}`;\n }\n\n const result = await runBruno({\n root: collectionDir,\n target: effectiveTarget,\n ...(opts.env ? { environment: opts.env } : {}),\n log: (msg) => {\n process.stdout.write(`${msg}\\n`);\n },\n });\n process.exit(result.code);\n },\n );\n\n program\n .command(\"use\")\n .description(\"Set the default CF context (region/org/space/app) for future `run` calls\")\n .argument(\"<shorthand>\", \"region/org/space/app\")\n .option(\"--no-verify\", \"Skip verifying the context against the cached CF structure\")\n .action(async (shorthand: string, opts: { verify?: boolean }): Promise<void> => {\n const ctx = await useContext({\n shorthand,\n verify: opts.verify !== false,\n });\n process.stdout.write(`โœ” Default context set to ${ctx.region}/${ctx.org}/${ctx.space}/${ctx.app}\\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","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { brunoContextPath } from \"./paths.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport async function readContext(): Promise<BrunoContext | undefined> {\n try {\n const raw = await readFile(brunoContextPath(), \"utf8\");\n return JSON.parse(raw) as BrunoContext;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n}\n\nexport async function writeContext(ctx: Omit<BrunoContext, \"updatedAt\">): Promise<BrunoContext> {\n const updated: BrunoContext = { ...ctx, updatedAt: new Date().toISOString() };\n const path = brunoContextPath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(updated, null, 2)}\\n`, \"utf8\");\n return updated;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const BRUNO_CONTEXT_FILENAME = \"bruno-context.json\";\n\nexport const REGION_FOLDER_PREFIX = \"region__\";\nexport const ORG_FOLDER_PREFIX = \"org__\";\nexport const SPACE_FOLDER_PREFIX = \"space__\";\nexport const ENVIRONMENTS_DIR = \"environments\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function brunoContextPath(): string {\n return join(saptoolsDir(), BRUNO_CONTEXT_FILENAME);\n}\n\nexport function regionFolderName(key: string): string {\n return `${REGION_FOLDER_PREFIX}${key}`;\n}\n\nexport function orgFolderName(name: string): string {\n return `${ORG_FOLDER_PREFIX}${name}`;\n}\n\nexport function spaceFolderName(name: string): string {\n return `${SPACE_FOLDER_PREFIX}${name}`;\n}\n\nexport function parsePrefixedName(\n dirName: string,\n prefix: string,\n): string | undefined {\n if (!dirName.startsWith(prefix)) {\n return undefined;\n }\n return dirName.slice(prefix.length);\n}\n","import { Separator, checkbox, input } from \"@inquirer/prompts\";\n\nimport type { EnvironmentSelection } from \"./setup-app.js\";\nimport { assertValidEnvName } from \"./setup-app.js\";\n\nconst ADD_CUSTOM_ENVIRONMENT = \"__saptools_add_custom_environment__\";\n\ninterface EnvironmentChoice {\n readonly value: string;\n readonly name: string;\n readonly checked?: boolean;\n readonly description?: string;\n}\n\ninterface CheckboxChoiceState {\n readonly value: string;\n}\n\ninterface EnvironmentPromptDeps {\n readonly checkboxPrompt?: (config: {\n message: string;\n choices: readonly (Separator | EnvironmentChoice)[];\n validate: (choices: readonly CheckboxChoiceState[]) => boolean | string;\n }) => Promise<string[]>;\n readonly inputPrompt?: (config: {\n message: string;\n default: string;\n validate: (value: string) => boolean | string;\n }) => Promise<string>;\n}\n\nfunction uniqueNames(names: readonly string[]): string[] {\n const merged: string[] = [];\n for (const name of names) {\n if (!merged.includes(name)) {\n merged.push(name);\n }\n }\n return merged;\n}\n\nfunction validateEnvironmentSelection(choices: readonly CheckboxChoiceState[]): boolean | string {\n const selected = choices.map((choice) => choice.value);\n const hasEnvironment = selected.some((value) => value !== ADD_CUSTOM_ENVIRONMENT);\n if (hasEnvironment || selected.includes(ADD_CUSTOM_ENVIRONMENT)) {\n return true;\n }\n return 'Select at least one environment, or choose \"Add custom environment\".';\n}\n\nfunction buildEnvironmentChoices(\n names: readonly string[],\n selected: ReadonlySet<string>,\n): readonly (Separator | EnvironmentChoice)[] {\n return [\n ...names.map((name) => ({\n value: name,\n name,\n checked: selected.has(name),\n })),\n new Separator(),\n {\n value: ADD_CUSTOM_ENVIRONMENT,\n name: \"Add custom environment\",\n description: \"Create another environment name and return to this menu\",\n },\n ];\n}\n\nfunction validateCustomEnvironmentName(value: string): boolean | string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n return true;\n }\n\n try {\n assertValidEnvName(trimmed);\n return true;\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n}\n\nexport async function promptForEnvironments(\n opts: EnvironmentSelection,\n deps: EnvironmentPromptDeps = {},\n): Promise<readonly string[]> {\n const checkboxPrompt = deps.checkboxPrompt ?? checkbox;\n const inputPrompt = deps.inputPrompt ?? input;\n const selected = new Set<string>(opts.existing);\n const customNames: string[] = [];\n\n for (;;) {\n const names = uniqueNames([...opts.common, ...opts.existing, ...customNames]);\n const answers = await checkboxPrompt({\n message: \"Environments to create (space to toggle, enter to continue)\",\n choices: buildEnvironmentChoices(names, selected),\n validate: validateEnvironmentSelection,\n });\n\n selected.clear();\n for (const name of answers) {\n if (name !== ADD_CUSTOM_ENVIRONMENT) {\n selected.add(name);\n }\n }\n\n if (!answers.includes(ADD_CUSTOM_ENVIRONMENT)) {\n return [...selected];\n }\n\n const custom = (await inputPrompt({\n message: \"Custom environment name (leave empty to go back)\",\n default: \"\",\n validate: validateCustomEnvironmentName,\n })).trim();\n\n if (custom.length === 0) {\n continue;\n }\n\n if (!customNames.includes(custom) && !names.includes(custom)) {\n customNames.push(custom);\n }\n selected.add(custom);\n }\n}\n\nexport const environmentPromptTestHelpers = {\n buildEnvironmentChoices,\n validateEnvironmentSelection,\n validateCustomEnvironmentName,\n};\n","import { mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport type { OrgNode, RegionKey, RegionNode, SpaceNode } from \"@saptools/cf-sync\";\n\nimport type { CfInfoDeps } from \"./cf-info.js\";\nimport { defaultCfInfoDeps, listRegionsWithContent } from \"./cf-info.js\";\nimport { writeCfMetaToFile } from \"./cf-meta.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\nimport type { CfAppRef } from \"./types.js\";\n\nexport interface EnvironmentSelection {\n readonly common: readonly string[];\n readonly existing: readonly string[];\n}\n\nexport interface SetupAppPrompts {\n readonly selectRegion: (choices: readonly { value: RegionKey; name: string }[]) => Promise<RegionKey>;\n readonly selectOrg: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectSpace: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectApp: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly confirmCreate: (path: string) => Promise<boolean>;\n readonly selectEnvironments: (opts: EnvironmentSelection) => Promise<readonly string[]>;\n}\n\nexport interface SetupAppOptions {\n readonly root: string;\n readonly prompts: SetupAppPrompts;\n readonly deps?: CfInfoDeps;\n readonly log?: (msg: string) => void;\n}\n\nexport interface SetupAppResult {\n readonly ref: CfAppRef;\n readonly appPath: string;\n readonly environments: readonly string[];\n readonly created: boolean;\n}\n\nexport const COMMON_ENVIRONMENTS = [\"local\", \"dev\", \"staging\", \"prod\"] as const;\n\nexport const ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;\n\nexport function assertValidEnvName(name: string): void {\n if (!ENV_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid environment name '${name}': only letters, digits, dot, underscore, and dash are allowed.`,\n );\n }\n}\n\nfunction emptyEnvContent(envName: string, ref: CfAppRef): string {\n const lines = [\n \"vars {\",\n ` __cf_region: ${ref.region}`,\n ` __cf_org: ${ref.org}`,\n ` __cf_space: ${ref.space}`,\n ` __cf_app: ${ref.app}`,\n ` environment: ${envName}`,\n \" baseUrl: \",\n \"}\",\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\nasync function ensureEnvFile(appPath: string, envName: string, ref: CfAppRef): Promise<string> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n await mkdir(envDir, { recursive: true });\n const filePath = join(envDir, `${envName}.bru`);\n try {\n await writeFile(filePath, emptyEnvContent(envName, ref), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n await writeCfMetaToFile(filePath, ref);\n }\n return filePath;\n}\n\nfunction pickRegion(regions: readonly { key: RegionKey; label: string; orgCount: number }[]): readonly {\n readonly value: RegionKey;\n readonly name: string;\n}[] {\n return regions.map((r) => ({ value: r.key, name: `${r.key} โ€” ${r.label} (${r.orgCount.toString()} org${r.orgCount === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickOrg(region: RegionNode): readonly { value: string; name: string }[] {\n return region.orgs.map((o) => ({ value: o.name, name: `${o.name} (${o.spaces.length.toString()} space${o.spaces.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickSpace(org: OrgNode): readonly { value: string; name: string }[] {\n return org.spaces.map((s) => ({ value: s.name, name: `${s.name} (${s.apps.length.toString()} app${s.apps.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickApp(space: SpaceNode): readonly { value: string; name: string }[] {\n return space.apps.map((a) => ({ value: a.name, name: a.name }));\n}\n\nexport async function setupApp(options: SetupAppOptions): Promise<SetupAppResult> {\n const deps = options.deps ?? defaultCfInfoDeps;\n const log = options.log ?? ((): void => undefined);\n\n const regions = await listRegionsWithContent(deps);\n if (regions.length === 0) {\n throw new Error(\n \"No CF regions with orgs are cached. Run `cf-sync sync` first, or pass SAP_EMAIL/SAP_PASSWORD to refresh.\",\n );\n }\n\n const regionKey = await options.prompts.selectRegion(pickRegion(regions));\n const regionView = await deps.readRegionView(regionKey);\n if (!regionView) {\n throw new Error(`Region ${regionKey} is not cached. Run \\`cf-sync sync\\` or \\`cf-sync region ${regionKey}\\`.`);\n }\n const region = regionView.region;\n\n if (region.orgs.length === 0) {\n throw new Error(`Region ${regionKey} has no accessible orgs.`);\n }\n\n const orgName = await options.prompts.selectOrg(pickOrg(region));\n const org = region.orgs.find((o) => o.name === orgName);\n if (!org) {\n throw new Error(`Org ${orgName} not found in region ${regionKey}`);\n }\n if (org.spaces.length === 0) {\n throw new Error(`Org ${orgName} has no spaces.`);\n }\n\n const spaceName = await options.prompts.selectSpace(pickSpace(org));\n const space = org.spaces.find((s) => s.name === spaceName);\n if (!space) {\n throw new Error(`Space ${spaceName} not found in org ${orgName}`);\n }\n if (space.apps.length === 0) {\n throw new Error(`Space ${spaceName} has no apps.`);\n }\n\n const appName = await options.prompts.selectApp(pickApp(space));\n const ref: CfAppRef = { region: regionKey, org: orgName, space: spaceName, app: appName };\n\n const appPath = join(\n options.root,\n regionFolderName(regionKey),\n orgFolderName(orgName),\n spaceFolderName(spaceName),\n appName,\n );\n\n const confirmed = await options.prompts.confirmCreate(appPath);\n if (!confirmed) {\n return { ref, appPath, environments: [], created: false };\n }\n\n await mkdir(appPath, { recursive: true });\n\n const existingEnvs = await listExistingEnvs(appPath);\n const common = [...COMMON_ENVIRONMENTS];\n const selected = await options.prompts.selectEnvironments({ common, existing: existingEnvs });\n const merged: string[] = [];\n for (const name of selected) {\n const trimmed = name.trim();\n if (trimmed.length === 0 || merged.includes(trimmed)) {\n continue;\n }\n assertValidEnvName(trimmed);\n merged.push(trimmed);\n }\n if (merged.length === 0) {\n throw new Error(\"At least one environment is required.\");\n }\n\n const created: string[] = [];\n for (const envName of merged) {\n const path = await ensureEnvFile(appPath, envName, ref);\n created.push(path);\n log(`โ€ข ${path}`);\n }\n\n return { ref, appPath, environments: created, created: true };\n}\n\nasync function listExistingEnvs(appPath: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(join(appPath, ENVIRONMENTS_DIR), { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith(\".bru\"))\n .map((e) => e.name.replace(/\\.bru$/, \"\"));\n } catch {\n return [];\n }\n}\n","import type {\n CfStructure,\n OrgNode,\n RegionKey,\n RegionNode,\n RegionsView,\n RegionView,\n SpaceNode,\n StructureView,\n} from \"@saptools/cf-sync\";\nimport {\n getRegionView as getRegionViewApi,\n readRegionsView,\n readRegionView,\n readStructureView,\n REGION_KEYS,\n} from \"@saptools/cf-sync\";\n\nexport interface CfInfoDeps {\n readonly readStructureView: () => Promise<StructureView | undefined>;\n readonly readRegionsView: () => Promise<RegionsView>;\n readonly readRegionView: (key: RegionKey) => Promise<RegionView | undefined>;\n readonly getRegionView: (opts: {\n readonly regionKey: RegionKey;\n readonly email?: string;\n readonly password?: string;\n readonly refreshIfMissing?: boolean;\n }) => Promise<RegionView | undefined>;\n}\n\nexport const defaultCfInfoDeps: CfInfoDeps = {\n readStructureView,\n readRegionsView,\n readRegionView,\n getRegionView: getRegionViewApi,\n};\n\nexport function isValidRegionKey(value: string): value is RegionKey {\n return (REGION_KEYS as readonly string[]).includes(value);\n}\n\nexport interface StructureSnapshot {\n readonly source: \"runtime\" | \"stable\" | \"empty\";\n readonly structure: CfStructure | undefined;\n readonly stale: boolean;\n readonly message: string | undefined;\n}\n\nexport async function getStructureSnapshot(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<StructureSnapshot> {\n const view = await deps.readStructureView();\n if (!view) {\n return {\n source: \"empty\",\n structure: undefined,\n stale: true,\n message: \"No CF structure cached. Run `cf-sync sync` first.\",\n };\n }\n\n const stale = view.source === \"runtime\" && view.metadata?.status === \"running\";\n return {\n source: view.source,\n structure: view.structure,\n stale,\n message: stale ? \"A CF sync is still running โ€” showing partial data.\" : undefined,\n };\n}\n\nexport interface RegionSuggestion {\n readonly key: RegionKey;\n readonly label: string;\n readonly orgCount: number;\n}\n\nexport async function listRegionsWithContent(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<readonly RegionSuggestion[]> {\n const snapshot = await getStructureSnapshot(deps);\n if (!snapshot.structure) {\n return [];\n }\n return snapshot.structure.regions\n .filter((r) => r.accessible && r.orgs.length > 0)\n .map((r) => ({ key: r.key, label: r.label, orgCount: r.orgs.length }));\n}\n\nexport async function getRegion(\n key: RegionKey,\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<RegionNode | undefined> {\n const view = await deps.readRegionView(key);\n return view?.region;\n}\n\nexport function findOrg(region: RegionNode, orgName: string): OrgNode | undefined {\n return region.orgs.find((o) => o.name === orgName);\n}\n\nexport function findSpace(org: OrgNode, spaceName: string): SpaceNode | undefined {\n return org.spaces.find((s) => s.name === spaceName);\n}\n\nexport function findApp(space: SpaceNode, appName: string): { readonly name: string } | undefined {\n return space.apps.find((a) => a.name === appName);\n}\n\nexport interface ResolvedRef {\n readonly region: RegionNode;\n readonly org: OrgNode;\n readonly space: SpaceNode;\n readonly app: { readonly name: string };\n}\n\nexport async function resolveRef(\n ref: {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n },\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<ResolvedRef | undefined> {\n const region = await getRegion(ref.region, deps);\n if (!region) {\n return undefined;\n }\n const org = findOrg(region, ref.org);\n if (!org) {\n return undefined;\n }\n const space = findSpace(org, ref.space);\n if (!space) {\n return undefined;\n }\n const app = findApp(space, ref.app);\n if (!app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport { upsertVars } from \"./bru-writer.js\";\nimport { CF_META_KEYS } from \"./types.js\";\nimport type { CfAppRef, CfMetaKey } from \"./types.js\";\n\nexport interface CfMeta {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport function readCfMetaFromVars(vars: ReadonlyMap<string, string>): CfMeta | undefined {\n const region = vars.get(\"__cf_region\");\n const org = vars.get(\"__cf_org\");\n const space = vars.get(\"__cf_space\");\n const app = vars.get(\"__cf_app\");\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport function buildCfMetaUpdates(ref: CfAppRef, baseUrl?: string): Map<string, string> {\n const updates = new Map<string, string>();\n const pairs: readonly [CfMetaKey, string][] = [\n [\"__cf_region\", ref.region],\n [\"__cf_org\", ref.org],\n [\"__cf_space\", ref.space],\n [\"__cf_app\", ref.app],\n ];\n for (const [k, v] of pairs) {\n updates.set(k, v);\n }\n if (baseUrl !== undefined) {\n updates.set(\"baseUrl\", baseUrl);\n }\n return updates;\n}\n\nexport function hasCfMeta(vars: ReadonlyMap<string, string>): boolean {\n return CF_META_KEYS.every((k) => {\n const v = vars.get(k);\n return v !== undefined && v.length > 0;\n });\n}\n\nexport async function readCfMetaFromFile(path: string): Promise<CfMeta | undefined> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return readCfMetaFromVars(parsed.vars.entries);\n}\n\nexport async function writeCfMetaToFile(\n path: string,\n ref: CfAppRef,\n baseUrl?: string,\n): Promise<boolean> {\n const raw = await readFile(path, \"utf8\");\n const updates = buildCfMetaUpdates(ref, baseUrl);\n const { content, changed } = upsertVars(raw, updates);\n if (changed) {\n await writeFile(path, content, \"utf8\");\n }\n return changed;\n}\n","import type { BruVarsBlock } from \"./types.js\";\n\ninterface BlockRange {\n readonly header: string;\n readonly start: number;\n readonly end: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n readonly open: \"{\" | \"[\";\n readonly close: \"}\" | \"]\";\n}\n\nconst HEADER_REGEX = /(^|\\n)\\s*([a-zA-Z][a-zA-Z0-9:_-]*)\\s*([{[])/g;\n\nfunction findMatchingClose(raw: string, open: \"{\" | \"[\", openIdx: number): number {\n const close = open === \"{\" ? \"}\" : \"]\";\n let depth = 1;\n let i = openIdx + 1;\n while (i < raw.length) {\n const ch = raw[i];\n if (ch === open) {\n depth++;\n } else if (ch === close) {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n i++;\n }\n return -1;\n}\n\nexport function listBlocks(raw: string): readonly BlockRange[] {\n const blocks: BlockRange[] = [];\n HEADER_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = HEADER_REGEX.exec(raw)) !== null) {\n const leadingNewline = match[1] ?? \"\";\n const header = match[2];\n const open = match[3];\n if (header === undefined || (open !== \"{\" && open !== \"[\")) {\n continue;\n }\n const headerStart = match.index + leadingNewline.length;\n const openIdx = match.index + match[0].length - 1;\n const closeIdx = findMatchingClose(raw, open, openIdx);\n if (closeIdx === -1) {\n continue;\n }\n blocks.push({\n header,\n start: headerStart,\n end: closeIdx + 1,\n bodyStart: openIdx + 1,\n bodyEnd: closeIdx,\n open,\n close: open === \"{\" ? \"}\" : \"]\",\n });\n }\n return blocks;\n}\n\nexport function parseKeyValueBody(body: string): Map<string, string> {\n const entries = new Map<string, string>();\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n continue;\n }\n const key = line.slice(0, colon).trim();\n const value = line.slice(colon + 1).trim();\n if (key.length > 0) {\n entries.set(key, value);\n }\n }\n return entries;\n}\n\nexport function parseListBody(body: string): string[] {\n const items: string[] = [];\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n items.push(line);\n }\n return items;\n}\n\nexport interface ParsedBruEnv {\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport function parseBruEnvFile(raw: string): ParsedBruEnv {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n const entries = varsBlock\n ? parseKeyValueBody(raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd))\n : new Map<string, string>();\n const secrets = secretsBlock\n ? parseListBody(raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd))\n : [];\n\n return { vars: { entries }, secrets };\n}\n","import { listBlocks, parseKeyValueBody } from \"./bru-parser.js\";\n\nexport interface UpsertResult {\n readonly content: string;\n readonly changed: boolean;\n}\n\nfunction formatVarsBlock(entries: ReadonlyMap<string, string>): string {\n const lines: string[] = [];\n for (const [key, value] of entries) {\n lines.push(` ${key}: ${value}`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function upsertVars(\n raw: string,\n updates: ReadonlyMap<string, string>,\n): UpsertResult {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n\n if (!varsBlock) {\n const newBlock = `vars {\\n${formatVarsBlock(updates)}\\n}\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: updates.size > 0 };\n }\n\n const body = raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd);\n const existing = parseKeyValueBody(body);\n let changed = false;\n for (const [k, v] of updates) {\n if (existing.get(k) !== v) {\n existing.set(k, v);\n changed = true;\n }\n }\n\n if (!changed) {\n return { content: raw, changed: false };\n }\n\n const rebuilt = `\\n${formatVarsBlock(existing)}\\n`;\n const before = raw.slice(0, varsBlock.bodyStart);\n const after = raw.slice(varsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n\nexport function ensureSecretEntry(raw: string, secretName: string): UpsertResult {\n const blocks = listBlocks(raw);\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n if (!secretsBlock) {\n const newBlock = `vars:secret [\\n ${secretName}\\n]\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: true };\n }\n\n const body = raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd);\n const items = body\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0 && !l.startsWith(\"//\"));\n if (items.includes(secretName)) {\n return { content: raw, changed: false };\n }\n items.push(secretName);\n const rebuilt = `\\n ${items.join(\"\\n \")}\\n`;\n const before = raw.slice(0, secretsBlock.bodyStart);\n const after = raw.slice(secretsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\nimport type { AppRef } from \"@saptools/cf-xsuaa\";\nimport { getTokenCached as getTokenCachedApi } from \"@saptools/cf-xsuaa\";\n\nimport { readCfMetaFromFile } from \"./cf-meta.js\";\nimport type { ShorthandRef } from \"./folder-scan.js\";\nimport { parseShorthandPath, scanCollection } from \"./folder-scan.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\n\nexport type GetTokenCachedFn = (ref: AppRef) => Promise<string>;\n\nexport interface RunSpawnResult {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport type SpawnBruFn = (\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n) => Promise<RunSpawnResult>;\n\nexport interface RunOptions {\n readonly root: string;\n readonly target: string;\n readonly environment?: string;\n readonly extraArgs?: readonly string[];\n readonly getTokenCached?: GetTokenCachedFn;\n readonly spawnBru?: SpawnBruFn;\n readonly log?: (msg: string) => void;\n}\n\nexport interface RunPlan {\n readonly filePath: string;\n readonly environment: string;\n readonly envFile: string;\n readonly meta: { readonly region: string; readonly org: string; readonly space: string; readonly app: string };\n readonly token: string;\n readonly bruArgs: readonly string[];\n readonly cwd: string;\n}\n\nexport interface RunResult extends RunPlan {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nconst require = createRequire(import.meta.url);\n\nexport interface BruRuntime {\n readonly command: string;\n readonly argsPrefix: readonly string[];\n}\n\nexport interface ResolveBruRuntimeDeps {\n readonly findOnPath?: (command: string, env: NodeJS.ProcessEnv) => Promise<string | undefined>;\n readonly readTextFile?: (path: string) => Promise<string>;\n readonly resolvePackageJsonPath?: () => string;\n}\n\nfunction pathEntries(env: NodeJS.ProcessEnv): readonly string[] {\n const value = env[\"PATH\"] ?? process.env[\"PATH\"] ?? \"\";\n return value.split(delimiter).filter((entry) => entry.length > 0);\n}\n\nfunction pathCandidates(command: string, env: NodeJS.ProcessEnv): readonly string[] {\n if (process.platform !== \"win32\" || command.includes(\".\")) {\n return [command];\n }\n const pathExt =\n env[\"PATHEXT\"]?.split(\";\").filter((entry) => entry.length > 0) ?? [\".COM\", \".EXE\", \".BAT\", \".CMD\"];\n return [command, ...pathExt.map((ext) => `${command}${ext}`)];\n}\n\nasync function findCommandOnPath(command: string, env: NodeJS.ProcessEnv): Promise<string | undefined> {\n const candidates = pathCandidates(command, env);\n for (const entry of pathEntries(env)) {\n for (const candidate of candidates) {\n const fullPath = join(entry, candidate);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction bruBinRelativePath(value: unknown): string | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const bin = value[\"bin\"];\n if (typeof bin === \"string\") {\n return bin;\n }\n if (!isRecord(bin)) {\n return undefined;\n }\n const bru = bin[\"bru\"];\n return typeof bru === \"string\" ? bru : undefined;\n}\n\nfunction defaultResolvePackageJsonPath(): string {\n return require.resolve(\"@usebruno/cli/package.json\");\n}\n\nasync function defaultReadTextFile(path: string): Promise<string> {\n return await readFile(path, \"utf8\");\n}\n\nasync function resolveBundledBruBinPath(\n deps: ResolveBruRuntimeDeps,\n): Promise<string | undefined> {\n try {\n const packageJsonPath = (deps.resolvePackageJsonPath ?? defaultResolvePackageJsonPath)();\n const raw = await (deps.readTextFile ?? defaultReadTextFile)(packageJsonPath);\n const binPath = bruBinRelativePath(JSON.parse(raw) as unknown);\n if (!binPath) {\n return undefined;\n }\n return resolve(dirname(packageJsonPath), binPath);\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveBruRuntime(\n env: NodeJS.ProcessEnv = process.env,\n deps: ResolveBruRuntimeDeps = {},\n): Promise<BruRuntime> {\n const onPath = await (deps.findOnPath ?? findCommandOnPath)(\"bru\", env);\n if (onPath) {\n return { command: onPath, argsPrefix: [] };\n }\n const bundledBin = await resolveBundledBruBinPath(deps);\n if (bundledBin) {\n return { command: process.execPath, argsPrefix: [bundledBin] };\n }\n throw new Error(\n \"Unable to find Bruno CLI. Install @usebruno/cli or ensure `bru` is available on PATH.\",\n );\n}\n\nasync function defaultSpawnBru(\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n): Promise<RunSpawnResult> {\n const runtime = await resolveBruRuntime(env);\n return await new Promise((resolvePromise, rejectPromise) => {\n const child = spawn(runtime.command, [...runtime.argsPrefix, ...args], { cwd, env, stdio: \"pipe\" });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf8\");\n process.stdout.write(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n process.stderr.write(chunk);\n });\n child.on(\"error\", rejectPromise);\n child.on(\"close\", (code) => {\n resolvePromise({ code: code ?? 0, stdout, stderr });\n });\n });\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveTarget(\n root: string,\n target: string,\n): Promise<{ readonly filePath: string; readonly shorthand: ShorthandRef | undefined }> {\n const direct = isAbsolute(target) ? target : resolve(process.cwd(), target);\n if (await exists(direct)) {\n return { filePath: direct, shorthand: undefined };\n }\n\n const shorthand = parseShorthandPath(target);\n if (!shorthand) {\n throw new Error(`Target not found: ${target}`);\n }\n\n const { region, org, space, app, filePath } = shorthand;\n const appDir = join(\n root,\n regionFolderName(region),\n orgFolderName(org),\n spaceFolderName(space),\n app,\n );\n\n if (!filePath) {\n return { filePath: appDir, shorthand };\n }\n\n const candidate = join(appDir, filePath);\n if (await exists(candidate)) {\n return { filePath: candidate, shorthand };\n }\n\n const withExt = candidate.endsWith(\".bru\") ? candidate : `${candidate}.bru`;\n if (await exists(withExt)) {\n return { filePath: withExt, shorthand };\n }\n\n throw new Error(`File not found: ${candidate}`);\n}\n\nasync function chooseEnvironmentFile(\n appDir: string,\n environment: string | undefined,\n): Promise<{ readonly envFile: string; readonly environment: string }> {\n if (environment) {\n const envFile = join(appDir, ENVIRONMENTS_DIR, `${environment}.bru`);\n if (!(await exists(envFile))) {\n throw new Error(`Environment file not found: ${envFile}`);\n }\n return { envFile, environment };\n }\n\n const collection = await scanCollection(resolve(appDir, \"..\", \"..\", \"..\", \"..\"));\n for (const region of collection.regions) {\n for (const org of region.orgs) {\n for (const space of org.spaces) {\n for (const app of space.apps) {\n if (app.path === appDir && app.environments.length > 0) {\n const first = app.environments[0];\n if (first) {\n return { envFile: first.path, environment: first.name };\n }\n }\n }\n }\n }\n }\n throw new Error(`No environment files found under ${appDir}/${ENVIRONMENTS_DIR}`);\n}\n\nfunction findAppDirFromFile(filePath: string, root: string): string {\n const rel = relative(root, filePath).split(sep);\n if (rel.length < 4) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n const [regionDir, orgDir, spaceDir, appDir] = rel;\n if (!regionDir || !orgDir || !spaceDir || !appDir) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n return join(root, regionDir, orgDir, spaceDir, appDir);\n}\n\nexport async function buildRunPlan(options: RunOptions): Promise<RunPlan> {\n const { filePath } = await resolveTarget(options.root, options.target);\n const stats = await stat(filePath);\n\n let appDir: string;\n let requestFile: string | undefined;\n\n if (stats.isDirectory()) {\n appDir = filePath;\n requestFile = undefined;\n } else {\n appDir = findAppDirFromFile(filePath, options.root);\n requestFile = filePath;\n }\n\n const { envFile, environment } = await chooseEnvironmentFile(appDir, options.environment);\n\n const meta = await readCfMetaFromFile(envFile);\n if (!meta) {\n throw new Error(\n `Missing __cf_region/__cf_org/__cf_space/__cf_app in ${envFile}. Run \\`saptools-bruno setup-app\\` first.`,\n );\n }\n\n const getToken = options.getTokenCached ?? getTokenCachedApi;\n const token = await getToken(meta);\n\n const bruArgs: string[] = [\"run\"];\n if (requestFile) {\n bruArgs.push(relative(appDir, requestFile) || \".\");\n }\n bruArgs.push(\"--env\", environment, \"--env-var\", `accessToken=${token}`);\n if (options.extraArgs) {\n bruArgs.push(...options.extraArgs);\n }\n\n return {\n filePath,\n environment,\n envFile,\n meta,\n token,\n bruArgs,\n cwd: appDir,\n };\n}\n\nexport async function runBruno(options: RunOptions): Promise<RunResult> {\n const plan = await buildRunPlan(options);\n const spawnFn = options.spawnBru ?? defaultSpawnBru;\n const env: NodeJS.ProcessEnv = { ...process.env, SAPTOOLS_ACCESS_TOKEN: plan.token };\n options.log?.(`โ–ถ bru ${plan.bruArgs.join(\" \")} (cwd=${plan.cwd})`);\n const result = await spawnFn(plan.bruArgs, env, plan.cwd);\n return { ...plan, ...result };\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport {\n ENVIRONMENTS_DIR,\n ORG_FOLDER_PREFIX,\n parsePrefixedName,\n REGION_FOLDER_PREFIX,\n SPACE_FOLDER_PREFIX,\n} from \"./paths.js\";\nimport type {\n AppFolder,\n BrunoCollection,\n BruEnvFile,\n OrgFolder,\n RegionFolder,\n SpaceFolder,\n} from \"./types.js\";\n\nasync function safeReaddir(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function listFiles(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function loadEnvFile(path: string, name: string): Promise<BruEnvFile> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return {\n path,\n name: name.replace(/\\.bru$/, \"\"),\n raw,\n vars: parsed.vars,\n secrets: parsed.secrets,\n };\n}\n\nasync function scanAppEnvironments(appPath: string): Promise<readonly BruEnvFile[]> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n const files = await listFiles(envDir);\n const bruFiles = files.filter((f) => f.endsWith(\".bru\"));\n const loaded: BruEnvFile[] = [];\n for (const file of bruFiles) {\n loaded.push(await loadEnvFile(join(envDir, file), file));\n }\n return loaded;\n}\n\nasync function scanApp(spacePath: string, name: string): Promise<AppFolder> {\n const appPath = join(spacePath, name);\n const environments = await scanAppEnvironments(appPath);\n return { path: appPath, name, environments };\n}\n\nasync function scanSpace(orgPath: string, dirName: string): Promise<SpaceFolder | undefined> {\n const name = parsePrefixedName(dirName, SPACE_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const spacePath = join(orgPath, dirName);\n const appDirs = await safeReaddir(spacePath);\n const apps: AppFolder[] = [];\n for (const appDir of appDirs) {\n apps.push(await scanApp(spacePath, appDir));\n }\n return { path: spacePath, name, apps };\n}\n\nasync function scanOrg(regionPath: string, dirName: string): Promise<OrgFolder | undefined> {\n const name = parsePrefixedName(dirName, ORG_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const orgPath = join(regionPath, dirName);\n const spaceDirs = await safeReaddir(orgPath);\n const spaces: SpaceFolder[] = [];\n for (const spaceDir of spaceDirs) {\n const space = await scanSpace(orgPath, spaceDir);\n if (space) {\n spaces.push(space);\n }\n }\n return { path: orgPath, name, spaces };\n}\n\nasync function scanRegion(root: string, dirName: string): Promise<RegionFolder | undefined> {\n const key = parsePrefixedName(dirName, REGION_FOLDER_PREFIX);\n if (key === undefined) {\n return undefined;\n }\n const regionPath = join(root, dirName);\n const orgDirs = await safeReaddir(regionPath);\n const orgs: OrgFolder[] = [];\n for (const orgDir of orgDirs) {\n const org = await scanOrg(regionPath, orgDir);\n if (org) {\n orgs.push(org);\n }\n }\n return { path: regionPath, key, orgs };\n}\n\nexport async function scanCollection(root: string): Promise<BrunoCollection> {\n const regionDirs = await safeReaddir(root);\n const regions: RegionFolder[] = [];\n for (const dir of regionDirs) {\n const region = await scanRegion(root, dir);\n if (region) {\n regions.push(region);\n }\n }\n return { root, regions };\n}\n\nexport interface ShorthandRef {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly environment?: string;\n readonly filePath?: string;\n}\n\nexport function parseShorthandPath(shorthand: string): ShorthandRef | undefined {\n const cleaned = shorthand.replace(/^[./]+/, \"\").replace(/\\\\/g, \"/\");\n const segs = cleaned.split(\"/\").filter((s) => s.length > 0);\n if (segs.length < 4) {\n return undefined;\n }\n const [region, org, space, app, ...rest] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n if (rest.length === 0) {\n return { region, org, space, app };\n }\n const filePath = rest.join(\"/\");\n const last = rest[rest.length - 1] ?? \"\";\n const environment = last.endsWith(\".bru\") ? last.replace(/\\.bru$/, \"\") : undefined;\n return environment\n ? { region, org, space, app, environment, filePath }\n : { region, org, space, app, filePath };\n}\n","import type { CfInfoDeps } from \"./cf-info.js\";\nimport { isValidRegionKey, resolveRef } from \"./cf-info.js\";\nimport { writeContext } from \"./context.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport function parseContextShorthand(shorthand: string): {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n} | undefined {\n const segs = shorthand.split(\"/\").filter((s) => s.length > 0);\n if (segs.length !== 4) {\n return undefined;\n }\n const [region, org, space, app] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport interface UseOptions {\n readonly shorthand: string;\n readonly deps?: CfInfoDeps;\n readonly verify?: boolean;\n}\n\nexport async function useContext(options: UseOptions): Promise<BrunoContext> {\n const parsed = parseContextShorthand(options.shorthand);\n if (!parsed) {\n throw new Error(\n `Invalid context shorthand: ${options.shorthand}. Expected <region>/<org>/<space>/<app>.`,\n );\n }\n\n if (!isValidRegionKey(parsed.region)) {\n throw new Error(`Unknown region key: ${parsed.region}`);\n }\n\n if (options.verify !== false) {\n const resolved = await resolveRef({ ...parsed, region: parsed.region }, options.deps);\n if (!resolved) {\n throw new Error(\n `Could not verify ${options.shorthand} against the cached CF structure. Run \\`cf-sync sync\\` first.`,\n );\n }\n }\n\n return await writeContext(parsed);\n}\n"],"mappings":"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","names":["process","mkdir","writeFile","join","readFile","writeFile","sep","readFile","writeFile","join","mkdir","writeFile","readFile","dirname","join","readdir","readFile","join","readdir","readFile","join","require","join","readFile","dirname","process"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/context.ts","../src/paths.ts","../src/environment-prompt.ts","../src/setup-app.ts","../src/cf-info.ts","../src/cf-meta.ts","../src/bru-parser.ts","../src/bru-writer.ts","../src/run.ts","../src/folder-scan.ts","../src/use.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { confirm, select } from \"@inquirer/prompts\";\nimport { Command, Option } from \"commander\";\n\nimport { readContext } from \"./context.js\";\nimport { promptForEnvironments } from \"./environment-prompt.js\";\nimport { runBruno } from \"./run.js\";\nimport { setupApp } from \"./setup-app.js\";\nimport { useContext } from \"./use.js\";\n\nfunction resolveCollectionDir(explicitCollection: string | undefined, explicitRoot: string | undefined): string {\n if (explicitCollection) {\n return explicitCollection;\n }\n if (explicitRoot) {\n return explicitRoot;\n }\n if (process.env[\"SAPTOOLS_BRUNO_COLLECTION\"]) {\n return process.env[\"SAPTOOLS_BRUNO_COLLECTION\"];\n }\n if (process.env[\"SAPTOOLS_BRUNO_ROOT\"]) {\n return process.env[\"SAPTOOLS_BRUNO_ROOT\"];\n }\n return process.cwd();\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"saptools-bruno\")\n .description(\"Smart runner for Bruno with CF-aware env metadata and automatic token injection\")\n .addOption(new Option(\"--collection <dir>\", \"Bruno collection directory (default: SAPTOOLS_BRUNO_COLLECTION or cwd)\"))\n .addOption(new Option(\"--root <dir>\", \"Legacy alias for --collection\").hideHelp());\n\n program\n .command(\"setup-app\")\n .description(\"Interactively scaffold a bruno app folder and seed __cf_* variables\")\n .action(async (): Promise<void> => {\n const collectionDir = resolveCollectionDir(\n program.opts<{ collection?: string; root?: string }>().collection,\n program.opts<{ collection?: string; root?: string }>().root,\n );\n const result = await setupApp({\n root: collectionDir,\n prompts: {\n selectRegion: async (choices) => await select({ message: \"Select region\", choices: [...choices] }),\n selectOrg: async (choices) => await select({ message: \"Select org\", choices: [...choices] }),\n selectSpace: async (choices) => await select({ message: \"Select space\", choices: [...choices] }),\n selectApp: async (choices) => await select({ message: \"Select app\", choices: [...choices] }),\n confirmCreate: async (path) => await confirm({ message: `Create ${path}?`, default: true }),\n selectEnvironments: async (opts) => await promptForEnvironments(opts),\n },\n log: (msg) => {\n process.stdout.write(`${msg}\\n`);\n },\n });\n if (!result.created) {\n process.stdout.write(\"Aborted.\\n\");\n return;\n }\n process.stdout.write(`โœ” App folder ready at ${result.appPath}\\n`);\n });\n\n program\n .command(\"run\")\n .description(\"Run a bruno request or folder, auto-injecting an XSUAA token\")\n .argument(\"[target]\", \"Shorthand path (region/org/space/app[/folder/file.bru]) or real path\")\n .option(\"-e, --env <name>\", \"Environment name (default: context or first)\")\n .action(\n async (\n target: string | undefined,\n opts: { env?: string },\n ): Promise<void> => {\n const collectionDir = resolveCollectionDir(\n program.opts<{ collection?: string; root?: string }>().collection,\n program.opts<{ collection?: string; root?: string }>().root,\n );\n let effectiveTarget = target;\n\n if (!effectiveTarget) {\n const ctx = await readContext();\n if (!ctx) {\n throw new Error(\n \"No target specified and no default context is set. Run `saptools-bruno use <region/org/space/app>` first.\",\n );\n }\n effectiveTarget = `${ctx.region}/${ctx.org}/${ctx.space}/${ctx.app}`;\n }\n\n const result = await runBruno({\n root: collectionDir,\n target: effectiveTarget,\n ...(opts.env ? { environment: opts.env } : {}),\n log: (msg) => {\n process.stdout.write(`${msg}\\n`);\n },\n });\n process.exit(result.code);\n },\n );\n\n program\n .command(\"use\")\n .description(\"Set the default CF context (region/org/space/app) for future `run` calls\")\n .argument(\"<shorthand>\", \"region/org/space/app\")\n .option(\"--no-verify\", \"Skip verifying the context against the cached CF structure\")\n .action(async (shorthand: string, opts: { verify?: boolean }): Promise<void> => {\n const ctx = await useContext({\n shorthand,\n verify: opts.verify !== false,\n });\n process.stdout.write(`โœ” Default context set to ${ctx.region}/${ctx.org}/${ctx.space}/${ctx.app}\\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","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { brunoContextPath } from \"./paths.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport async function readContext(): Promise<BrunoContext | undefined> {\n try {\n const raw = await readFile(brunoContextPath(), \"utf8\");\n return JSON.parse(raw) as BrunoContext;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n}\n\nexport async function writeContext(ctx: Omit<BrunoContext, \"updatedAt\">): Promise<BrunoContext> {\n const updated: BrunoContext = { ...ctx, updatedAt: new Date().toISOString() };\n const path = brunoContextPath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(updated, null, 2)}\\n`, \"utf8\");\n return updated;\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const BRUNO_CONTEXT_FILENAME = \"bruno-context.json\";\n\nexport const REGION_FOLDER_PREFIX = \"region__\";\nexport const ORG_FOLDER_PREFIX = \"org__\";\nexport const SPACE_FOLDER_PREFIX = \"space__\";\nexport const ENVIRONMENTS_DIR = \"environments\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function brunoContextPath(): string {\n return join(saptoolsDir(), BRUNO_CONTEXT_FILENAME);\n}\n\nexport function regionFolderName(key: string): string {\n return `${REGION_FOLDER_PREFIX}${key}`;\n}\n\nexport function orgFolderName(name: string): string {\n return `${ORG_FOLDER_PREFIX}${name}`;\n}\n\nexport function spaceFolderName(name: string): string {\n return `${SPACE_FOLDER_PREFIX}${name}`;\n}\n\nexport function parsePrefixedName(\n dirName: string,\n prefix: string,\n): string | undefined {\n if (!dirName.startsWith(prefix)) {\n return undefined;\n }\n return dirName.slice(prefix.length);\n}\n","import { Separator, checkbox, input } from \"@inquirer/prompts\";\n\nimport type { EnvironmentSelection } from \"./setup-app.js\";\nimport { assertValidEnvName } from \"./setup-app.js\";\n\nconst ADD_CUSTOM_ENVIRONMENT = \"__saptools_add_custom_environment__\";\n\ninterface EnvironmentChoice {\n readonly value: string;\n readonly name: string;\n readonly checked?: boolean;\n readonly description?: string;\n}\n\ninterface CheckboxChoiceState {\n readonly value: string;\n}\n\ninterface EnvironmentPromptDeps {\n readonly checkboxPrompt?: (config: {\n message: string;\n choices: readonly (Separator | EnvironmentChoice)[];\n validate: (choices: readonly CheckboxChoiceState[]) => boolean | string;\n }) => Promise<string[]>;\n readonly inputPrompt?: (config: {\n message: string;\n default: string;\n validate: (value: string) => boolean | string;\n }) => Promise<string>;\n}\n\nfunction uniqueNames(names: readonly string[]): string[] {\n const merged: string[] = [];\n for (const name of names) {\n if (!merged.includes(name)) {\n merged.push(name);\n }\n }\n return merged;\n}\n\nfunction validateEnvironmentSelection(choices: readonly CheckboxChoiceState[]): boolean | string {\n const selected = choices.map((choice) => choice.value);\n const hasEnvironment = selected.some((value) => value !== ADD_CUSTOM_ENVIRONMENT);\n if (hasEnvironment || selected.includes(ADD_CUSTOM_ENVIRONMENT)) {\n return true;\n }\n return 'Select at least one environment, or choose \"Add custom environment\".';\n}\n\nfunction buildEnvironmentChoices(\n names: readonly string[],\n selected: ReadonlySet<string>,\n): readonly (Separator | EnvironmentChoice)[] {\n return [\n ...names.map((name) => ({\n value: name,\n name,\n checked: selected.has(name),\n })),\n new Separator(),\n {\n value: ADD_CUSTOM_ENVIRONMENT,\n name: \"Add custom environment\",\n description: \"Create another environment name and return to this menu\",\n },\n ];\n}\n\nfunction validateCustomEnvironmentName(value: string): boolean | string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n return true;\n }\n\n try {\n assertValidEnvName(trimmed);\n return true;\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n}\n\nexport async function promptForEnvironments(\n opts: EnvironmentSelection,\n deps: EnvironmentPromptDeps = {},\n): Promise<readonly string[]> {\n const checkboxPrompt = deps.checkboxPrompt ?? checkbox;\n const inputPrompt = deps.inputPrompt ?? input;\n const selected = new Set<string>(opts.existing);\n const customNames: string[] = [];\n\n for (;;) {\n const names = uniqueNames([...opts.common, ...opts.existing, ...customNames]);\n const answers = await checkboxPrompt({\n message: \"Environments to create (space to toggle, enter to continue)\",\n choices: buildEnvironmentChoices(names, selected),\n validate: validateEnvironmentSelection,\n });\n\n selected.clear();\n for (const name of answers) {\n if (name !== ADD_CUSTOM_ENVIRONMENT) {\n selected.add(name);\n }\n }\n\n if (!answers.includes(ADD_CUSTOM_ENVIRONMENT)) {\n return [...selected];\n }\n\n const custom = (await inputPrompt({\n message: \"Custom environment name (leave empty to go back)\",\n default: \"\",\n validate: validateCustomEnvironmentName,\n })).trim();\n\n if (custom.length === 0) {\n continue;\n }\n\n if (!customNames.includes(custom) && !names.includes(custom)) {\n customNames.push(custom);\n }\n selected.add(custom);\n }\n}\n\nexport const environmentPromptTestHelpers = {\n buildEnvironmentChoices,\n validateEnvironmentSelection,\n validateCustomEnvironmentName,\n};\n","import { mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\n\nimport type { OrgNode, RegionKey, RegionNode, SpaceNode } from \"@saptools/cf-sync\";\n\nimport type { CfInfoDeps } from \"./cf-info.js\";\nimport { defaultCfInfoDeps, listRegionsWithContent } from \"./cf-info.js\";\nimport { writeCfMetaToFile } from \"./cf-meta.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\nimport type { CfAppRef } from \"./types.js\";\n\nexport interface EnvironmentSelection {\n readonly common: readonly string[];\n readonly existing: readonly string[];\n}\n\nexport interface SetupAppPrompts {\n readonly selectRegion: (choices: readonly { value: RegionKey; name: string }[]) => Promise<RegionKey>;\n readonly selectOrg: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectSpace: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectApp: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly confirmCreate: (path: string) => Promise<boolean>;\n readonly selectEnvironments: (opts: EnvironmentSelection) => Promise<readonly string[]>;\n}\n\nexport interface SetupAppOptions {\n readonly root: string;\n readonly prompts: SetupAppPrompts;\n readonly deps?: CfInfoDeps;\n readonly log?: (msg: string) => void;\n}\n\nexport interface SetupAppResult {\n readonly ref: CfAppRef;\n readonly appPath: string;\n readonly environments: readonly string[];\n readonly created: boolean;\n}\n\nexport const COMMON_ENVIRONMENTS = [\"local\", \"dev\", \"staging\", \"prod\"] as const;\nconst BRUNO_COLLECTION_CONFIG_FILENAME = \"bruno.json\";\n\nexport const ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;\n\nexport function assertValidEnvName(name: string): void {\n if (!ENV_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid environment name '${name}': only letters, digits, dot, underscore, and dash are allowed.`,\n );\n }\n}\n\nfunction emptyEnvContent(envName: string, ref: CfAppRef): string {\n const lines = [\n \"vars {\",\n ` __cf_region: ${ref.region}`,\n ` __cf_org: ${ref.org}`,\n ` __cf_space: ${ref.space}`,\n ` __cf_app: ${ref.app}`,\n ` environment: ${envName}`,\n \" baseUrl: \",\n \"}\",\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\nfunction normalizeCollectionName(root: string): string {\n const candidate = basename(root).replace(/^\\.+/, \"\").trim();\n return candidate.length > 0 ? candidate : \"bruno-collection\";\n}\n\nfunction defaultBrunoConfig(root: string): string {\n return `${JSON.stringify(\n {\n version: \"1\",\n name: normalizeCollectionName(root),\n type: \"collection\",\n ignore: [\"node_modules\", \".git\"],\n },\n null,\n 2,\n )}\\n`;\n}\n\nasync function ensureCollectionConfig(root: string): Promise<void> {\n const filePath = join(root, BRUNO_COLLECTION_CONFIG_FILENAME);\n try {\n await writeFile(filePath, defaultBrunoConfig(root), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n }\n}\n\nasync function ensureEnvFile(appPath: string, envName: string, ref: CfAppRef): Promise<string> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n await mkdir(envDir, { recursive: true });\n const filePath = join(envDir, `${envName}.bru`);\n try {\n await writeFile(filePath, emptyEnvContent(envName, ref), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n await writeCfMetaToFile(filePath, ref);\n }\n return filePath;\n}\n\nfunction pickRegion(regions: readonly { key: RegionKey; label: string; orgCount: number }[]): readonly {\n readonly value: RegionKey;\n readonly name: string;\n}[] {\n return regions.map((r) => ({ value: r.key, name: `${r.key} โ€” ${r.label} (${r.orgCount.toString()} org${r.orgCount === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickOrg(region: RegionNode): readonly { value: string; name: string }[] {\n return region.orgs.map((o) => ({ value: o.name, name: `${o.name} (${o.spaces.length.toString()} space${o.spaces.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickSpace(org: OrgNode): readonly { value: string; name: string }[] {\n return org.spaces.map((s) => ({ value: s.name, name: `${s.name} (${s.apps.length.toString()} app${s.apps.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickApp(space: SpaceNode): readonly { value: string; name: string }[] {\n return space.apps.map((a) => ({ value: a.name, name: a.name }));\n}\n\nexport async function setupApp(options: SetupAppOptions): Promise<SetupAppResult> {\n const deps = options.deps ?? defaultCfInfoDeps;\n const log = options.log ?? ((): void => undefined);\n\n const regions = await listRegionsWithContent(deps);\n if (regions.length === 0) {\n throw new Error(\n \"No CF regions with orgs are cached. Run `cf-sync sync` first, or pass SAP_EMAIL/SAP_PASSWORD to refresh.\",\n );\n }\n\n const regionKey = await options.prompts.selectRegion(pickRegion(regions));\n const regionView = await deps.readRegionView(regionKey);\n if (!regionView) {\n throw new Error(`Region ${regionKey} is not cached. Run \\`cf-sync sync\\` or \\`cf-sync region ${regionKey}\\`.`);\n }\n const region = regionView.region;\n\n if (region.orgs.length === 0) {\n throw new Error(`Region ${regionKey} has no accessible orgs.`);\n }\n\n const orgName = await options.prompts.selectOrg(pickOrg(region));\n const org = region.orgs.find((o) => o.name === orgName);\n if (!org) {\n throw new Error(`Org ${orgName} not found in region ${regionKey}`);\n }\n if (org.spaces.length === 0) {\n throw new Error(`Org ${orgName} has no spaces.`);\n }\n\n const spaceName = await options.prompts.selectSpace(pickSpace(org));\n const space = org.spaces.find((s) => s.name === spaceName);\n if (!space) {\n throw new Error(`Space ${spaceName} not found in org ${orgName}`);\n }\n if (space.apps.length === 0) {\n throw new Error(`Space ${spaceName} has no apps.`);\n }\n\n const appName = await options.prompts.selectApp(pickApp(space));\n const ref: CfAppRef = { region: regionKey, org: orgName, space: spaceName, app: appName };\n\n const appPath = join(\n options.root,\n regionFolderName(regionKey),\n orgFolderName(orgName),\n spaceFolderName(spaceName),\n appName,\n );\n\n const confirmed = await options.prompts.confirmCreate(appPath);\n if (!confirmed) {\n return { ref, appPath, environments: [], created: false };\n }\n\n await mkdir(options.root, { recursive: true });\n await ensureCollectionConfig(options.root);\n await mkdir(appPath, { recursive: true });\n\n const existingEnvs = await listExistingEnvs(appPath);\n const common = [...COMMON_ENVIRONMENTS];\n const selected = await options.prompts.selectEnvironments({ common, existing: existingEnvs });\n const merged: string[] = [];\n for (const name of selected) {\n const trimmed = name.trim();\n if (trimmed.length === 0 || merged.includes(trimmed)) {\n continue;\n }\n assertValidEnvName(trimmed);\n merged.push(trimmed);\n }\n if (merged.length === 0) {\n throw new Error(\"At least one environment is required.\");\n }\n\n const created: string[] = [];\n for (const envName of merged) {\n const path = await ensureEnvFile(appPath, envName, ref);\n created.push(path);\n log(`โ€ข ${path}`);\n }\n\n return { ref, appPath, environments: created, created: true };\n}\n\nasync function listExistingEnvs(appPath: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(join(appPath, ENVIRONMENTS_DIR), { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith(\".bru\"))\n .map((e) => e.name.replace(/\\.bru$/, \"\"));\n } catch {\n return [];\n }\n}\n","import type {\n CfStructure,\n OrgNode,\n RegionKey,\n RegionNode,\n RegionsView,\n RegionView,\n SpaceNode,\n StructureView,\n} from \"@saptools/cf-sync\";\nimport {\n getRegionView as getRegionViewApi,\n readRegionsView,\n readRegionView,\n readStructureView,\n REGION_KEYS,\n} from \"@saptools/cf-sync\";\n\nexport interface CfInfoDeps {\n readonly readStructureView: () => Promise<StructureView | undefined>;\n readonly readRegionsView: () => Promise<RegionsView>;\n readonly readRegionView: (key: RegionKey) => Promise<RegionView | undefined>;\n readonly getRegionView: (opts: {\n readonly regionKey: RegionKey;\n readonly email?: string;\n readonly password?: string;\n readonly refreshIfMissing?: boolean;\n }) => Promise<RegionView | undefined>;\n}\n\nexport const defaultCfInfoDeps: CfInfoDeps = {\n readStructureView,\n readRegionsView,\n readRegionView,\n getRegionView: getRegionViewApi,\n};\n\nexport function isValidRegionKey(value: string): value is RegionKey {\n return (REGION_KEYS as readonly string[]).includes(value);\n}\n\nexport interface StructureSnapshot {\n readonly source: \"runtime\" | \"stable\" | \"empty\";\n readonly structure: CfStructure | undefined;\n readonly stale: boolean;\n readonly message: string | undefined;\n}\n\nexport async function getStructureSnapshot(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<StructureSnapshot> {\n const view = await deps.readStructureView();\n if (!view) {\n return {\n source: \"empty\",\n structure: undefined,\n stale: true,\n message: \"No CF structure cached. Run `cf-sync sync` first.\",\n };\n }\n\n const stale = view.source === \"runtime\" && view.metadata?.status === \"running\";\n return {\n source: view.source,\n structure: view.structure,\n stale,\n message: stale ? \"A CF sync is still running โ€” showing partial data.\" : undefined,\n };\n}\n\nexport interface RegionSuggestion {\n readonly key: RegionKey;\n readonly label: string;\n readonly orgCount: number;\n}\n\nexport async function listRegionsWithContent(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<readonly RegionSuggestion[]> {\n const snapshot = await getStructureSnapshot(deps);\n if (!snapshot.structure) {\n return [];\n }\n return snapshot.structure.regions\n .filter((r) => r.accessible && r.orgs.length > 0)\n .map((r) => ({ key: r.key, label: r.label, orgCount: r.orgs.length }));\n}\n\nexport async function getRegion(\n key: RegionKey,\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<RegionNode | undefined> {\n const view = await deps.readRegionView(key);\n return view?.region;\n}\n\nexport function findOrg(region: RegionNode, orgName: string): OrgNode | undefined {\n return region.orgs.find((o) => o.name === orgName);\n}\n\nexport function findSpace(org: OrgNode, spaceName: string): SpaceNode | undefined {\n return org.spaces.find((s) => s.name === spaceName);\n}\n\nexport function findApp(space: SpaceNode, appName: string): { readonly name: string } | undefined {\n return space.apps.find((a) => a.name === appName);\n}\n\nexport interface ResolvedRef {\n readonly region: RegionNode;\n readonly org: OrgNode;\n readonly space: SpaceNode;\n readonly app: { readonly name: string };\n}\n\nexport async function resolveRef(\n ref: {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n },\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<ResolvedRef | undefined> {\n const region = await getRegion(ref.region, deps);\n if (!region) {\n return undefined;\n }\n const org = findOrg(region, ref.org);\n if (!org) {\n return undefined;\n }\n const space = findSpace(org, ref.space);\n if (!space) {\n return undefined;\n }\n const app = findApp(space, ref.app);\n if (!app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport { upsertVars } from \"./bru-writer.js\";\nimport { CF_META_KEYS } from \"./types.js\";\nimport type { CfAppRef, CfMetaKey } from \"./types.js\";\n\nexport interface CfMeta {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport function readCfMetaFromVars(vars: ReadonlyMap<string, string>): CfMeta | undefined {\n const region = vars.get(\"__cf_region\");\n const org = vars.get(\"__cf_org\");\n const space = vars.get(\"__cf_space\");\n const app = vars.get(\"__cf_app\");\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport function buildCfMetaUpdates(ref: CfAppRef, baseUrl?: string): Map<string, string> {\n const updates = new Map<string, string>();\n const pairs: readonly [CfMetaKey, string][] = [\n [\"__cf_region\", ref.region],\n [\"__cf_org\", ref.org],\n [\"__cf_space\", ref.space],\n [\"__cf_app\", ref.app],\n ];\n for (const [k, v] of pairs) {\n updates.set(k, v);\n }\n if (baseUrl !== undefined) {\n updates.set(\"baseUrl\", baseUrl);\n }\n return updates;\n}\n\nexport function hasCfMeta(vars: ReadonlyMap<string, string>): boolean {\n return CF_META_KEYS.every((k) => {\n const v = vars.get(k);\n return v !== undefined && v.length > 0;\n });\n}\n\nexport async function readCfMetaFromFile(path: string): Promise<CfMeta | undefined> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return readCfMetaFromVars(parsed.vars.entries);\n}\n\nexport async function writeCfMetaToFile(\n path: string,\n ref: CfAppRef,\n baseUrl?: string,\n): Promise<boolean> {\n const raw = await readFile(path, \"utf8\");\n const updates = buildCfMetaUpdates(ref, baseUrl);\n const { content, changed } = upsertVars(raw, updates);\n if (changed) {\n await writeFile(path, content, \"utf8\");\n }\n return changed;\n}\n","import type { BruVarsBlock } from \"./types.js\";\n\ninterface BlockRange {\n readonly header: string;\n readonly start: number;\n readonly end: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n readonly open: \"{\" | \"[\";\n readonly close: \"}\" | \"]\";\n}\n\nconst HEADER_REGEX = /(^|\\n)\\s*([a-zA-Z][a-zA-Z0-9:_-]*)\\s*([{[])/g;\n\nfunction findMatchingClose(raw: string, open: \"{\" | \"[\", openIdx: number): number {\n const close = open === \"{\" ? \"}\" : \"]\";\n let depth = 1;\n let i = openIdx + 1;\n while (i < raw.length) {\n const ch = raw[i];\n if (ch === open) {\n depth++;\n } else if (ch === close) {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n i++;\n }\n return -1;\n}\n\nexport function listBlocks(raw: string): readonly BlockRange[] {\n const blocks: BlockRange[] = [];\n HEADER_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = HEADER_REGEX.exec(raw)) !== null) {\n const leadingNewline = match[1] ?? \"\";\n const header = match[2];\n const open = match[3];\n if (header === undefined || (open !== \"{\" && open !== \"[\")) {\n continue;\n }\n const headerStart = match.index + leadingNewline.length;\n const openIdx = match.index + match[0].length - 1;\n const closeIdx = findMatchingClose(raw, open, openIdx);\n if (closeIdx === -1) {\n continue;\n }\n blocks.push({\n header,\n start: headerStart,\n end: closeIdx + 1,\n bodyStart: openIdx + 1,\n bodyEnd: closeIdx,\n open,\n close: open === \"{\" ? \"}\" : \"]\",\n });\n }\n return blocks;\n}\n\nexport function parseKeyValueBody(body: string): Map<string, string> {\n const entries = new Map<string, string>();\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n continue;\n }\n const key = line.slice(0, colon).trim();\n const value = line.slice(colon + 1).trim();\n if (key.length > 0) {\n entries.set(key, value);\n }\n }\n return entries;\n}\n\nexport function parseListBody(body: string): string[] {\n const items: string[] = [];\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n items.push(line);\n }\n return items;\n}\n\nexport interface ParsedBruEnv {\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport function parseBruEnvFile(raw: string): ParsedBruEnv {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n const entries = varsBlock\n ? parseKeyValueBody(raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd))\n : new Map<string, string>();\n const secrets = secretsBlock\n ? parseListBody(raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd))\n : [];\n\n return { vars: { entries }, secrets };\n}\n","import { listBlocks, parseKeyValueBody } from \"./bru-parser.js\";\n\nexport interface UpsertResult {\n readonly content: string;\n readonly changed: boolean;\n}\n\nfunction formatVarsBlock(entries: ReadonlyMap<string, string>): string {\n const lines: string[] = [];\n for (const [key, value] of entries) {\n lines.push(` ${key}: ${value}`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function upsertVars(\n raw: string,\n updates: ReadonlyMap<string, string>,\n): UpsertResult {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n\n if (!varsBlock) {\n const newBlock = `vars {\\n${formatVarsBlock(updates)}\\n}\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: updates.size > 0 };\n }\n\n const body = raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd);\n const existing = parseKeyValueBody(body);\n let changed = false;\n for (const [k, v] of updates) {\n if (existing.get(k) !== v) {\n existing.set(k, v);\n changed = true;\n }\n }\n\n if (!changed) {\n return { content: raw, changed: false };\n }\n\n const rebuilt = `\\n${formatVarsBlock(existing)}\\n`;\n const before = raw.slice(0, varsBlock.bodyStart);\n const after = raw.slice(varsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n\nexport function ensureSecretEntry(raw: string, secretName: string): UpsertResult {\n const blocks = listBlocks(raw);\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n if (!secretsBlock) {\n const newBlock = `vars:secret [\\n ${secretName}\\n]\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: true };\n }\n\n const body = raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd);\n const items = body\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0 && !l.startsWith(\"//\"));\n if (items.includes(secretName)) {\n return { content: raw, changed: false };\n }\n items.push(secretName);\n const rebuilt = `\\n ${items.join(\"\\n \")}\\n`;\n const before = raw.slice(0, secretsBlock.bodyStart);\n const after = raw.slice(secretsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\nimport type { AppRef } from \"@saptools/cf-xsuaa\";\nimport { getTokenCached as getTokenCachedApi } from \"@saptools/cf-xsuaa\";\n\nimport { readCfMetaFromFile } from \"./cf-meta.js\";\nimport type { ShorthandRef } from \"./folder-scan.js\";\nimport { parseShorthandPath, scanCollection } from \"./folder-scan.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\n\nexport type GetTokenCachedFn = (ref: AppRef) => Promise<string>;\n\nexport interface RunSpawnResult {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport type SpawnBruFn = (\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n) => Promise<RunSpawnResult>;\n\nexport interface RunOptions {\n readonly root: string;\n readonly target: string;\n readonly environment?: string;\n readonly extraArgs?: readonly string[];\n readonly getTokenCached?: GetTokenCachedFn;\n readonly spawnBru?: SpawnBruFn;\n readonly log?: (msg: string) => void;\n}\n\nexport interface RunPlan {\n readonly filePath: string;\n readonly environment: string;\n readonly envFile: string;\n readonly meta: { readonly region: string; readonly org: string; readonly space: string; readonly app: string };\n readonly token: string;\n readonly bruArgs: readonly string[];\n readonly cwd: string;\n}\n\nexport interface RunResult extends RunPlan {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nconst require = createRequire(import.meta.url);\n\nexport interface BruRuntime {\n readonly command: string;\n readonly argsPrefix: readonly string[];\n}\n\nexport interface ResolveBruRuntimeDeps {\n readonly findOnPath?: (command: string, env: NodeJS.ProcessEnv) => Promise<string | undefined>;\n readonly readTextFile?: (path: string) => Promise<string>;\n readonly resolvePackageJsonPath?: () => string;\n}\n\nfunction pathEntries(env: NodeJS.ProcessEnv): readonly string[] {\n const value = env[\"PATH\"] ?? process.env[\"PATH\"] ?? \"\";\n return value.split(delimiter).filter((entry) => entry.length > 0);\n}\n\nfunction pathCandidates(command: string, env: NodeJS.ProcessEnv): readonly string[] {\n if (process.platform !== \"win32\" || command.includes(\".\")) {\n return [command];\n }\n const pathExt =\n env[\"PATHEXT\"]?.split(\";\").filter((entry) => entry.length > 0) ?? [\".COM\", \".EXE\", \".BAT\", \".CMD\"];\n return [command, ...pathExt.map((ext) => `${command}${ext}`)];\n}\n\nasync function findCommandOnPath(command: string, env: NodeJS.ProcessEnv): Promise<string | undefined> {\n const candidates = pathCandidates(command, env);\n for (const entry of pathEntries(env)) {\n for (const candidate of candidates) {\n const fullPath = join(entry, candidate);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction bruBinRelativePath(value: unknown): string | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const bin = value[\"bin\"];\n if (typeof bin === \"string\") {\n return bin;\n }\n if (!isRecord(bin)) {\n return undefined;\n }\n const bru = bin[\"bru\"];\n return typeof bru === \"string\" ? bru : undefined;\n}\n\nfunction defaultResolvePackageJsonPath(): string {\n return require.resolve(\"@usebruno/cli/package.json\");\n}\n\nasync function defaultReadTextFile(path: string): Promise<string> {\n return await readFile(path, \"utf8\");\n}\n\nasync function resolveBundledBruBinPath(\n deps: ResolveBruRuntimeDeps,\n): Promise<string | undefined> {\n try {\n const packageJsonPath = (deps.resolvePackageJsonPath ?? defaultResolvePackageJsonPath)();\n const raw = await (deps.readTextFile ?? defaultReadTextFile)(packageJsonPath);\n const binPath = bruBinRelativePath(JSON.parse(raw) as unknown);\n if (!binPath) {\n return undefined;\n }\n return resolve(dirname(packageJsonPath), binPath);\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveBruRuntime(\n env: NodeJS.ProcessEnv = process.env,\n deps: ResolveBruRuntimeDeps = {},\n): Promise<BruRuntime> {\n const onPath = await (deps.findOnPath ?? findCommandOnPath)(\"bru\", env);\n if (onPath) {\n return { command: onPath, argsPrefix: [] };\n }\n const bundledBin = await resolveBundledBruBinPath(deps);\n if (bundledBin) {\n return { command: process.execPath, argsPrefix: [bundledBin] };\n }\n throw new Error(\n \"Unable to find Bruno CLI. Install @usebruno/cli or ensure `bru` is available on PATH.\",\n );\n}\n\nasync function defaultSpawnBru(\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n): Promise<RunSpawnResult> {\n const runtime = await resolveBruRuntime(env);\n return await new Promise((resolvePromise, rejectPromise) => {\n const child = spawn(runtime.command, [...runtime.argsPrefix, ...args], { cwd, env, stdio: \"pipe\" });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf8\");\n process.stdout.write(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n process.stderr.write(chunk);\n });\n child.on(\"error\", rejectPromise);\n child.on(\"close\", (code) => {\n resolvePromise({ code: code ?? 0, stdout, stderr });\n });\n });\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveTarget(\n root: string,\n target: string,\n): Promise<{ readonly filePath: string; readonly shorthand: ShorthandRef | undefined }> {\n const direct = isAbsolute(target) ? target : resolve(process.cwd(), target);\n if (await exists(direct)) {\n return { filePath: direct, shorthand: undefined };\n }\n\n const shorthand = parseShorthandPath(target);\n if (!shorthand) {\n throw new Error(`Target not found: ${target}`);\n }\n\n const { region, org, space, app, filePath } = shorthand;\n const appDir = join(\n root,\n regionFolderName(region),\n orgFolderName(org),\n spaceFolderName(space),\n app,\n );\n\n if (!filePath) {\n return { filePath: appDir, shorthand };\n }\n\n const candidate = join(appDir, filePath);\n if (await exists(candidate)) {\n return { filePath: candidate, shorthand };\n }\n\n const withExt = candidate.endsWith(\".bru\") ? candidate : `${candidate}.bru`;\n if (await exists(withExt)) {\n return { filePath: withExt, shorthand };\n }\n\n throw new Error(`File not found: ${candidate}`);\n}\n\nasync function chooseEnvironmentFile(\n appDir: string,\n environment: string | undefined,\n): Promise<{ readonly envFile: string; readonly environment: string }> {\n if (environment) {\n const envFile = join(appDir, ENVIRONMENTS_DIR, `${environment}.bru`);\n if (!(await exists(envFile))) {\n throw new Error(`Environment file not found: ${envFile}`);\n }\n return { envFile, environment };\n }\n\n const collection = await scanCollection(resolve(appDir, \"..\", \"..\", \"..\", \"..\"));\n for (const region of collection.regions) {\n for (const org of region.orgs) {\n for (const space of org.spaces) {\n for (const app of space.apps) {\n if (app.path === appDir && app.environments.length > 0) {\n const first = app.environments[0];\n if (first) {\n return { envFile: first.path, environment: first.name };\n }\n }\n }\n }\n }\n }\n throw new Error(`No environment files found under ${appDir}/${ENVIRONMENTS_DIR}`);\n}\n\nfunction findAppDirFromFile(filePath: string, root: string): string {\n const rel = relative(root, filePath).split(sep);\n if (rel.length < 4) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n const [regionDir, orgDir, spaceDir, appDir] = rel;\n if (!regionDir || !orgDir || !spaceDir || !appDir) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n return join(root, regionDir, orgDir, spaceDir, appDir);\n}\n\nexport async function buildRunPlan(options: RunOptions): Promise<RunPlan> {\n const { filePath } = await resolveTarget(options.root, options.target);\n const stats = await stat(filePath);\n\n let appDir: string;\n let requestFile: string | undefined;\n\n if (stats.isDirectory()) {\n appDir = filePath;\n requestFile = undefined;\n } else {\n appDir = findAppDirFromFile(filePath, options.root);\n requestFile = filePath;\n }\n\n const { envFile, environment } = await chooseEnvironmentFile(appDir, options.environment);\n\n const meta = await readCfMetaFromFile(envFile);\n if (!meta) {\n throw new Error(\n `Missing __cf_region/__cf_org/__cf_space/__cf_app in ${envFile}. Run \\`saptools-bruno setup-app\\` first.`,\n );\n }\n\n const getToken = options.getTokenCached ?? getTokenCachedApi;\n const token = await getToken(meta);\n\n const bruArgs: string[] = [\"run\"];\n if (requestFile) {\n bruArgs.push(relative(appDir, requestFile) || \".\");\n }\n bruArgs.push(\"--env\", environment, \"--env-var\", `accessToken=${token}`);\n if (options.extraArgs) {\n bruArgs.push(...options.extraArgs);\n }\n\n return {\n filePath,\n environment,\n envFile,\n meta,\n token,\n bruArgs,\n cwd: appDir,\n };\n}\n\nexport async function runBruno(options: RunOptions): Promise<RunResult> {\n const plan = await buildRunPlan(options);\n const spawnFn = options.spawnBru ?? defaultSpawnBru;\n const env: NodeJS.ProcessEnv = { ...process.env, SAPTOOLS_ACCESS_TOKEN: plan.token };\n options.log?.(`โ–ถ bru ${plan.bruArgs.join(\" \")} (cwd=${plan.cwd})`);\n const result = await spawnFn(plan.bruArgs, env, plan.cwd);\n return { ...plan, ...result };\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport {\n ENVIRONMENTS_DIR,\n ORG_FOLDER_PREFIX,\n parsePrefixedName,\n REGION_FOLDER_PREFIX,\n SPACE_FOLDER_PREFIX,\n} from \"./paths.js\";\nimport type {\n AppFolder,\n BrunoCollection,\n BruEnvFile,\n OrgFolder,\n RegionFolder,\n SpaceFolder,\n} from \"./types.js\";\n\nasync function safeReaddir(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function listFiles(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function loadEnvFile(path: string, name: string): Promise<BruEnvFile> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return {\n path,\n name: name.replace(/\\.bru$/, \"\"),\n raw,\n vars: parsed.vars,\n secrets: parsed.secrets,\n };\n}\n\nasync function scanAppEnvironments(appPath: string): Promise<readonly BruEnvFile[]> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n const files = await listFiles(envDir);\n const bruFiles = files.filter((f) => f.endsWith(\".bru\"));\n const loaded: BruEnvFile[] = [];\n for (const file of bruFiles) {\n loaded.push(await loadEnvFile(join(envDir, file), file));\n }\n return loaded;\n}\n\nasync function scanApp(spacePath: string, name: string): Promise<AppFolder> {\n const appPath = join(spacePath, name);\n const environments = await scanAppEnvironments(appPath);\n return { path: appPath, name, environments };\n}\n\nasync function scanSpace(orgPath: string, dirName: string): Promise<SpaceFolder | undefined> {\n const name = parsePrefixedName(dirName, SPACE_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const spacePath = join(orgPath, dirName);\n const appDirs = await safeReaddir(spacePath);\n const apps: AppFolder[] = [];\n for (const appDir of appDirs) {\n apps.push(await scanApp(spacePath, appDir));\n }\n return { path: spacePath, name, apps };\n}\n\nasync function scanOrg(regionPath: string, dirName: string): Promise<OrgFolder | undefined> {\n const name = parsePrefixedName(dirName, ORG_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const orgPath = join(regionPath, dirName);\n const spaceDirs = await safeReaddir(orgPath);\n const spaces: SpaceFolder[] = [];\n for (const spaceDir of spaceDirs) {\n const space = await scanSpace(orgPath, spaceDir);\n if (space) {\n spaces.push(space);\n }\n }\n return { path: orgPath, name, spaces };\n}\n\nasync function scanRegion(root: string, dirName: string): Promise<RegionFolder | undefined> {\n const key = parsePrefixedName(dirName, REGION_FOLDER_PREFIX);\n if (key === undefined) {\n return undefined;\n }\n const regionPath = join(root, dirName);\n const orgDirs = await safeReaddir(regionPath);\n const orgs: OrgFolder[] = [];\n for (const orgDir of orgDirs) {\n const org = await scanOrg(regionPath, orgDir);\n if (org) {\n orgs.push(org);\n }\n }\n return { path: regionPath, key, orgs };\n}\n\nexport async function scanCollection(root: string): Promise<BrunoCollection> {\n const regionDirs = await safeReaddir(root);\n const regions: RegionFolder[] = [];\n for (const dir of regionDirs) {\n const region = await scanRegion(root, dir);\n if (region) {\n regions.push(region);\n }\n }\n return { root, regions };\n}\n\nexport interface ShorthandRef {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly environment?: string;\n readonly filePath?: string;\n}\n\nexport function parseShorthandPath(shorthand: string): ShorthandRef | undefined {\n const cleaned = shorthand.replace(/^[./]+/, \"\").replace(/\\\\/g, \"/\");\n const segs = cleaned.split(\"/\").filter((s) => s.length > 0);\n if (segs.length < 4) {\n return undefined;\n }\n const [region, org, space, app, ...rest] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n if (rest.length === 0) {\n return { region, org, space, app };\n }\n const filePath = rest.join(\"/\");\n const last = rest[rest.length - 1] ?? \"\";\n const environment = last.endsWith(\".bru\") ? last.replace(/\\.bru$/, \"\") : undefined;\n return environment\n ? { region, org, space, app, environment, filePath }\n : { region, org, space, app, filePath };\n}\n","import type { CfInfoDeps } from \"./cf-info.js\";\nimport { isValidRegionKey, resolveRef } from \"./cf-info.js\";\nimport { writeContext } from \"./context.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport function parseContextShorthand(shorthand: string): {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n} | undefined {\n const segs = shorthand.split(\"/\").filter((s) => s.length > 0);\n if (segs.length !== 4) {\n return undefined;\n }\n const [region, org, space, app] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport interface UseOptions {\n readonly shorthand: string;\n readonly deps?: CfInfoDeps;\n readonly verify?: boolean;\n}\n\nexport async function useContext(options: UseOptions): Promise<BrunoContext> {\n const parsed = parseContextShorthand(options.shorthand);\n if (!parsed) {\n throw new Error(\n `Invalid context shorthand: ${options.shorthand}. Expected <region>/<org>/<space>/<app>.`,\n );\n }\n\n if (!isValidRegionKey(parsed.region)) {\n throw new Error(`Unknown region key: ${parsed.region}`);\n }\n\n if (options.verify !== false) {\n const resolved = await resolveRef({ ...parsed, region: parsed.region }, options.deps);\n if (!resolved) {\n throw new Error(\n `Could not verify ${options.shorthand} against the cached CF structure. Run \\`cf-sync sync\\` first.`,\n );\n }\n }\n\n return await writeContext(parsed);\n}\n"],"mappings":";;;AAAA,OAAOA,cAAa;AAEpB,SAAS,SAAS,cAAc;AAChC,SAAS,SAAS,cAAc;;;ACHhC,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,eAAe;;;ACDxB,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,IAAM,oBAAoB;AAC1B,IAAM,yBAAyB;AAE/B,IAAM,uBAAuB;AAC7B,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,mBAAmB;AAEzB,SAAS,cAAsB;AACpC,SAAO,KAAK,QAAQ,GAAG,iBAAiB;AAC1C;AAEO,SAAS,mBAA2B;AACzC,SAAO,KAAK,YAAY,GAAG,sBAAsB;AACnD;AAEO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,GAAG,oBAAoB,GAAG,GAAG;AACtC;AAEO,SAAS,cAAc,MAAsB;AAClD,SAAO,GAAG,iBAAiB,GAAG,IAAI;AACpC;AAEO,SAAS,gBAAgB,MAAsB;AACpD,SAAO,GAAG,mBAAmB,GAAG,IAAI;AACtC;AAEO,SAAS,kBACd,SACA,QACoB;AACpB,MAAI,CAAC,QAAQ,WAAW,MAAM,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,MAAM,OAAO,MAAM;AACpC;;;ADjCA,eAAsB,cAAiD;AACrE,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,iBAAiB,GAAG,MAAM;AACrD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,UAAU;AACpD,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,aAAa,KAA6D;AAC9F,QAAM,UAAwB,EAAE,GAAG,KAAK,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAC5E,QAAM,OAAO,iBAAiB;AAC9B,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACrE,SAAO;AACT;;;AExBA,SAAS,WAAW,UAAU,aAAa;;;ACA3C,SAAS,SAAAC,QAAO,SAAS,aAAAC,kBAAiB;AAC1C,SAAS,UAAU,QAAAC,aAAY;;;ACS/B;AAAA,EACE,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAcA,IAAM,oBAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB;AAEO,SAAS,iBAAiB,OAAmC;AAClE,SAAQ,YAAkC,SAAS,KAAK;AAC1D;AASA,eAAsB,qBACpB,OAAmB,mBACS;AAC5B,QAAM,OAAO,MAAM,KAAK,kBAAkB;AAC1C,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,QAAQ,KAAK,WAAW,aAAa,KAAK,UAAU,WAAW;AACrE,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,IAChB;AAAA,IACA,SAAS,QAAQ,4DAAuD;AAAA,EAC1E;AACF;AAQA,eAAsB,uBACpB,OAAmB,mBACmB;AACtC,QAAM,WAAW,MAAM,qBAAqB,IAAI;AAChD,MAAI,CAAC,SAAS,WAAW;AACvB,WAAO,CAAC;AAAA,EACV;AACA,SAAO,SAAS,UAAU,QACvB,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,SAAS,CAAC,EAC/C,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,OAAO,UAAU,EAAE,KAAK,OAAO,EAAE;AACzE;AAEA,eAAsB,UACpB,KACA,OAAmB,mBACc;AACjC,QAAM,OAAO,MAAM,KAAK,eAAe,GAAG;AAC1C,SAAO,MAAM;AACf;AAEO,SAAS,QAAQ,QAAoB,SAAsC;AAChF,SAAO,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AACnD;AAEO,SAAS,UAAU,KAAc,WAA0C;AAChF,SAAO,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AACpD;AAEO,SAAS,QAAQ,OAAkB,SAAwD;AAChG,SAAO,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AAClD;AASA,eAAsB,WACpB,KAMA,OAAmB,mBACe;AAClC,QAAM,SAAS,MAAM,UAAU,IAAI,QAAQ,IAAI;AAC/C,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,QAAM,MAAM,QAAQ,QAAQ,IAAI,GAAG;AACnC,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,UAAU,KAAK,IAAI,KAAK;AACtC,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AACA,QAAM,MAAM,QAAQ,OAAO,IAAI,GAAG;AAClC,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AACA,SAAO,EAAE,QAAQ,KAAK,OAAO,IAAI;AACnC;;;AC7IA,SAAS,YAAAC,WAAU,aAAAC,kBAAiB;;;ACYpC,IAAM,eAAe;AAErB,SAAS,kBAAkB,KAAa,MAAiB,SAAyB;AAChF,QAAM,QAAQ,SAAS,MAAM,MAAM;AACnC,MAAI,QAAQ;AACZ,MAAI,IAAI,UAAU;AAClB,SAAO,IAAI,IAAI,QAAQ;AACrB,UAAM,KAAK,IAAI,CAAC;AAChB,QAAI,OAAO,MAAM;AACf;AAAA,IACF,WAAW,OAAO,OAAO;AACvB;AACA,UAAI,UAAU,GAAG;AACf,eAAO;AAAA,MACT;AAAA,IACF;AACA;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,WAAW,KAAoC;AAC7D,QAAM,SAAuB,CAAC;AAC9B,eAAa,YAAY;AACzB,MAAI;AACJ,UAAQ,QAAQ,aAAa,KAAK,GAAG,OAAO,MAAM;AAChD,UAAM,iBAAiB,MAAM,CAAC,KAAK;AACnC,UAAM,SAAS,MAAM,CAAC;AACtB,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,WAAW,UAAc,SAAS,OAAO,SAAS,KAAM;AAC1D;AAAA,IACF;AACA,UAAM,cAAc,MAAM,QAAQ,eAAe;AACjD,UAAM,UAAU,MAAM,QAAQ,MAAM,CAAC,EAAE,SAAS;AAChD,UAAM,WAAW,kBAAkB,KAAK,MAAM,OAAO;AACrD,QAAI,aAAa,IAAI;AACnB;AAAA,IACF;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,OAAO;AAAA,MACP,KAAK,WAAW;AAAA,MAChB,WAAW,UAAU;AAAA,MACrB,SAAS;AAAA,MACT;AAAA,MACA,OAAO,SAAS,MAAM,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,MAAmC;AACnE,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,WAAW,KAAK,MAAM,IAAI,GAAG;AACtC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,KAAK,WAAW,KAAK,KAAK,WAAW,IAAI,GAAG;AAC9C;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,UAAU,IAAI;AAChB;AAAA,IACF;AACA,UAAM,MAAM,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK;AACtC,UAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK;AACzC,QAAI,IAAI,SAAS,GAAG;AAClB,cAAQ,IAAI,KAAK,KAAK;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,cAAc,MAAwB;AACpD,QAAM,QAAkB,CAAC;AACzB,aAAW,WAAW,KAAK,MAAM,IAAI,GAAG;AACtC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,KAAK,WAAW,KAAK,KAAK,WAAW,IAAI,GAAG;AAC9C;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AACA,SAAO;AACT;AAOO,SAAS,gBAAgB,KAA2B;AACzD,QAAM,SAAS,WAAW,GAAG;AAC7B,QAAM,YAAY,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE,SAAS,GAAG;AAC1E,QAAM,eAAe,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,iBAAiB,EAAE,SAAS,GAAG;AAEpF,QAAM,UAAU,YACZ,kBAAkB,IAAI,MAAM,UAAU,WAAW,UAAU,OAAO,CAAC,IACnE,oBAAI,IAAoB;AAC5B,QAAM,UAAU,eACZ,cAAc,IAAI,MAAM,aAAa,WAAW,aAAa,OAAO,CAAC,IACrE,CAAC;AAEL,SAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ;AACtC;;;AC1GA,SAAS,gBAAgB,SAA8C;AACrE,QAAM,QAAkB,CAAC;AACzB,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,UAAM,KAAK,KAAK,GAAG,KAAK,KAAK,EAAE;AAAA,EACjC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,WACd,KACA,SACc;AACd,QAAM,SAAS,WAAW,GAAG;AAC7B,QAAM,YAAY,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE,SAAS,GAAG;AAE1E,MAAI,CAAC,WAAW;AACd,UAAM,WAAW;AAAA,EAAW,gBAAgB,OAAO,CAAC;AAAA;AAAA;AACpD,UAAMC,OAAM,IAAI,SAAS,KAAK,CAAC,IAAI,SAAS,IAAI,IAAI,SAAS,IAAI,SAAS,IAAI,OAAO;AACrF,WAAO,EAAE,SAAS,GAAG,GAAG,GAAGA,IAAG,GAAG,QAAQ,IAAI,SAAS,QAAQ,OAAO,EAAE;AAAA,EACzE;AAEA,QAAM,OAAO,IAAI,MAAM,UAAU,WAAW,UAAU,OAAO;AAC7D,QAAM,WAAW,kBAAkB,IAAI;AACvC,MAAI,UAAU;AACd,aAAW,CAAC,GAAG,CAAC,KAAK,SAAS;AAC5B,QAAI,SAAS,IAAI,CAAC,MAAM,GAAG;AACzB,eAAS,IAAI,GAAG,CAAC;AACjB,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,SAAS,KAAK,SAAS,MAAM;AAAA,EACxC;AAEA,QAAM,UAAU;AAAA,EAAK,gBAAgB,QAAQ,CAAC;AAAA;AAC9C,QAAM,SAAS,IAAI,MAAM,GAAG,UAAU,SAAS;AAC/C,QAAM,QAAQ,IAAI,MAAM,UAAU,OAAO;AACzC,SAAO,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,IAAI,SAAS,KAAK;AACjE;;;AFhCO,SAAS,mBAAmB,MAAuD;AACxF,QAAM,SAAS,KAAK,IAAI,aAAa;AACrC,QAAM,MAAM,KAAK,IAAI,UAAU;AAC/B,QAAM,QAAQ,KAAK,IAAI,YAAY;AACnC,QAAM,MAAM,KAAK,IAAI,UAAU;AAC/B,MAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK;AACrC,WAAO;AAAA,EACT;AACA,SAAO,EAAE,QAAQ,KAAK,OAAO,IAAI;AACnC;AAEO,SAAS,mBAAmB,KAAe,SAAuC;AACvF,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,QAAwC;AAAA,IAC5C,CAAC,eAAe,IAAI,MAAM;AAAA,IAC1B,CAAC,YAAY,IAAI,GAAG;AAAA,IACpB,CAAC,cAAc,IAAI,KAAK;AAAA,IACxB,CAAC,YAAY,IAAI,GAAG;AAAA,EACtB;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO;AAC1B,YAAQ,IAAI,GAAG,CAAC;AAAA,EAClB;AACA,MAAI,YAAY,QAAW;AACzB,YAAQ,IAAI,WAAW,OAAO;AAAA,EAChC;AACA,SAAO;AACT;AASA,eAAsB,mBAAmB,MAA2C;AAClF,QAAM,MAAM,MAAMC,UAAS,MAAM,MAAM;AACvC,QAAM,SAAS,gBAAgB,GAAG;AAClC,SAAO,mBAAmB,OAAO,KAAK,OAAO;AAC/C;AAEA,eAAsB,kBACpB,MACA,KACA,SACkB;AAClB,QAAM,MAAM,MAAMA,UAAS,MAAM,MAAM;AACvC,QAAM,UAAU,mBAAmB,KAAK,OAAO;AAC/C,QAAM,EAAE,SAAS,QAAQ,IAAI,WAAW,KAAK,OAAO;AACpD,MAAI,SAAS;AACX,UAAMC,WAAU,MAAM,SAAS,MAAM;AAAA,EACvC;AACA,SAAO;AACT;;;AFvBO,IAAM,sBAAsB,CAAC,SAAS,OAAO,WAAW,MAAM;AACrE,IAAM,mCAAmC;AAElC,IAAM,mBAAmB;AAEzB,SAAS,mBAAmB,MAAoB;AACrD,MAAI,CAAC,iBAAiB,KAAK,IAAI,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,6BAA6B,IAAI;AAAA,IACnC;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,SAAiB,KAAuB;AAC/D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,kBAAkB,IAAI,MAAM;AAAA,IAC5B,eAAe,IAAI,GAAG;AAAA,IACtB,iBAAiB,IAAI,KAAK;AAAA,IAC1B,eAAe,IAAI,GAAG;AAAA,IACtB,kBAAkB,OAAO;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,wBAAwB,MAAsB;AACrD,QAAM,YAAY,SAAS,IAAI,EAAE,QAAQ,QAAQ,EAAE,EAAE,KAAK;AAC1D,SAAO,UAAU,SAAS,IAAI,YAAY;AAC5C;AAEA,SAAS,mBAAmB,MAAsB;AAChD,SAAO,GAAG,KAAK;AAAA,IACb;AAAA,MACE,SAAS;AAAA,MACT,MAAM,wBAAwB,IAAI;AAAA,MAClC,MAAM;AAAA,MACN,QAAQ,CAAC,gBAAgB,MAAM;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA;AACH;AAEA,eAAe,uBAAuB,MAA6B;AACjE,QAAM,WAAWC,MAAK,MAAM,gCAAgC;AAC5D,MAAI;AACF,UAAMC,WAAU,UAAU,mBAAmB,IAAI,GAAG,EAAE,UAAU,QAAQ,MAAM,KAAK,CAAC;AAAA,EACtF,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,UAAU;AACpD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,cAAc,SAAiB,SAAiB,KAAgC;AAC7F,QAAM,SAASD,MAAK,SAAS,gBAAgB;AAC7C,QAAME,OAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,QAAM,WAAWF,MAAK,QAAQ,GAAG,OAAO,MAAM;AAC9C,MAAI;AACF,UAAMC,WAAU,UAAU,gBAAgB,SAAS,GAAG,GAAG,EAAE,UAAU,QAAQ,MAAM,KAAK,CAAC;AAAA,EAC3F,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,UAAU;AACpD,YAAM;AAAA,IACR;AACA,UAAM,kBAAkB,UAAU,GAAG;AAAA,EACvC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,SAGhB;AACF,SAAO,QAAQ,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,GAAG,EAAE,GAAG,WAAM,EAAE,KAAK,KAAK,EAAE,SAAS,SAAS,CAAC,OAAO,EAAE,aAAa,IAAI,KAAK,GAAG,IAAI,EAAE;AAC1I;AAEA,SAAS,QAAQ,QAAgE;AAC/E,SAAO,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,OAAO,OAAO,SAAS,CAAC,SAAS,EAAE,OAAO,WAAW,IAAI,KAAK,GAAG,IAAI,EAAE;AAC/I;AAEA,SAAS,UAAU,KAA0D;AAC3E,SAAO,IAAI,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,KAAK,OAAO,SAAS,CAAC,OAAO,EAAE,KAAK,WAAW,IAAI,KAAK,GAAG,IAAI,EAAE;AACxI;AAEA,SAAS,QAAQ,OAA8D;AAC7E,SAAO,MAAM,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAChE;AAEA,eAAsB,SAAS,SAAmD;AAChF,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,MAAM,QAAQ,QAAQ,MAAY;AAExC,QAAM,UAAU,MAAM,uBAAuB,IAAI;AACjD,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,QAAQ,QAAQ,aAAa,WAAW,OAAO,CAAC;AACxE,QAAM,aAAa,MAAM,KAAK,eAAe,SAAS;AACtD,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,UAAU,SAAS,4DAA4D,SAAS,KAAK;AAAA,EAC/G;AACA,QAAM,SAAS,WAAW;AAE1B,MAAI,OAAO,KAAK,WAAW,GAAG;AAC5B,UAAM,IAAI,MAAM,UAAU,SAAS,0BAA0B;AAAA,EAC/D;AAEA,QAAM,UAAU,MAAM,QAAQ,QAAQ,UAAU,QAAQ,MAAM,CAAC;AAC/D,QAAM,MAAM,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AACtD,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,OAAO,OAAO,wBAAwB,SAAS,EAAE;AAAA,EACnE;AACA,MAAI,IAAI,OAAO,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM,OAAO,OAAO,iBAAiB;AAAA,EACjD;AAEA,QAAM,YAAY,MAAM,QAAQ,QAAQ,YAAY,UAAU,GAAG,CAAC;AAClE,QAAM,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AACzD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,SAAS,SAAS,qBAAqB,OAAO,EAAE;AAAA,EAClE;AACA,MAAI,MAAM,KAAK,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM,SAAS,SAAS,eAAe;AAAA,EACnD;AAEA,QAAM,UAAU,MAAM,QAAQ,QAAQ,UAAU,QAAQ,KAAK,CAAC;AAC9D,QAAM,MAAgB,EAAE,QAAQ,WAAW,KAAK,SAAS,OAAO,WAAW,KAAK,QAAQ;AAExF,QAAM,UAAUD;AAAA,IACd,QAAQ;AAAA,IACR,iBAAiB,SAAS;AAAA,IAC1B,cAAc,OAAO;AAAA,IACrB,gBAAgB,SAAS;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,QAAQ,QAAQ,cAAc,OAAO;AAC7D,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,KAAK,SAAS,cAAc,CAAC,GAAG,SAAS,MAAM;AAAA,EAC1D;AAEA,QAAME,OAAM,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAM,uBAAuB,QAAQ,IAAI;AACzC,QAAMA,OAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,QAAM,eAAe,MAAM,iBAAiB,OAAO;AACnD,QAAM,SAAS,CAAC,GAAG,mBAAmB;AACtC,QAAM,WAAW,MAAM,QAAQ,QAAQ,mBAAmB,EAAE,QAAQ,UAAU,aAAa,CAAC;AAC5F,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,UAAU;AAC3B,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,QAAQ,WAAW,KAAK,OAAO,SAAS,OAAO,GAAG;AACpD;AAAA,IACF;AACA,uBAAmB,OAAO;AAC1B,WAAO,KAAK,OAAO;AAAA,EACrB;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AAEA,QAAM,UAAoB,CAAC;AAC3B,aAAW,WAAW,QAAQ;AAC5B,UAAM,OAAO,MAAM,cAAc,SAAS,SAAS,GAAG;AACtD,YAAQ,KAAK,IAAI;AACjB,QAAI,UAAK,IAAI,EAAE;AAAA,EACjB;AAEA,SAAO,EAAE,KAAK,SAAS,cAAc,SAAS,SAAS,KAAK;AAC9D;AAEA,eAAe,iBAAiB,SAA6C;AAC3E,MAAI;AACF,UAAM,UAAU,MAAM,QAAQF,MAAK,SAAS,gBAAgB,GAAG,EAAE,eAAe,KAAK,CAAC;AACtF,WAAO,QACJ,OAAO,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,KAAK,SAAS,MAAM,CAAC,EACnD,IAAI,CAAC,MAAM,EAAE,KAAK,QAAQ,UAAU,EAAE,CAAC;AAAA,EAC5C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;ADjOA,IAAM,yBAAyB;AA0B/B,SAAS,YAAY,OAAoC;AACvD,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,OAAO,SAAS,IAAI,GAAG;AAC1B,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,6BAA6B,SAA2D;AAC/F,QAAM,WAAW,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK;AACrD,QAAM,iBAAiB,SAAS,KAAK,CAAC,UAAU,UAAU,sBAAsB;AAChF,MAAI,kBAAkB,SAAS,SAAS,sBAAsB,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,wBACP,OACA,UAC4C;AAC5C,SAAO;AAAA,IACL,GAAG,MAAM,IAAI,CAAC,UAAU;AAAA,MACtB,OAAO;AAAA,MACP;AAAA,MACA,SAAS,SAAS,IAAI,IAAI;AAAA,IAC5B,EAAE;AAAA,IACF,IAAI,UAAU;AAAA,IACd;AAAA,MACE,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,8BAA8B,OAAiC;AACtE,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,uBAAmB,OAAO;AAC1B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,WAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,EACxD;AACF;AAEA,eAAsB,sBACpB,MACA,OAA8B,CAAC,GACH;AAC5B,QAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,WAAW,IAAI,IAAY,KAAK,QAAQ;AAC9C,QAAM,cAAwB,CAAC;AAE/B,aAAS;AACP,UAAM,QAAQ,YAAY,CAAC,GAAG,KAAK,QAAQ,GAAG,KAAK,UAAU,GAAG,WAAW,CAAC;AAC5E,UAAM,UAAU,MAAM,eAAe;AAAA,MACnC,SAAS;AAAA,MACT,SAAS,wBAAwB,OAAO,QAAQ;AAAA,MAChD,UAAU;AAAA,IACZ,CAAC;AAED,aAAS,MAAM;AACf,eAAW,QAAQ,SAAS;AAC1B,UAAI,SAAS,wBAAwB;AACnC,iBAAS,IAAI,IAAI;AAAA,MACnB;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,SAAS,sBAAsB,GAAG;AAC7C,aAAO,CAAC,GAAG,QAAQ;AAAA,IACrB;AAEA,UAAM,UAAU,MAAM,YAAY;AAAA,MAChC,SAAS;AAAA,MACT,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC,GAAG,KAAK;AAET,QAAI,OAAO,WAAW,GAAG;AACvB;AAAA,IACF;AAEA,QAAI,CAAC,YAAY,SAAS,MAAM,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG;AAC5D,kBAAY,KAAK,MAAM;AAAA,IACzB;AACA,aAAS,IAAI,MAAM;AAAA,EACrB;AACF;;;AM9HA,SAAS,aAAa;AACtB,SAAS,YAAAG,WAAU,YAAY;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,WAAW,WAAAC,UAAS,YAAY,QAAAC,OAAM,UAAU,SAAS,WAAW;AAG7E,SAAS,kBAAkB,yBAAyB;;;ACNpD,SAAS,WAAAC,UAAS,YAAAC,iBAAgB;AAClC,SAAS,QAAAC,aAAY;AAmBrB,eAAe,YAAY,MAA0C;AACnE,MAAI;AACF,UAAM,UAAU,MAAMC,SAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC3D,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACjE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,UAAU,MAA0C;AACjE,MAAI;AACF,UAAM,UAAU,MAAMA,SAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC3D,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAC5D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,YAAY,MAAc,MAAmC;AAC1E,QAAM,MAAM,MAAMC,UAAS,MAAM,MAAM;AACvC,QAAM,SAAS,gBAAgB,GAAG;AAClC,SAAO;AAAA,IACL;AAAA,IACA,MAAM,KAAK,QAAQ,UAAU,EAAE;AAAA,IAC/B;AAAA,IACA,MAAM,OAAO;AAAA,IACb,SAAS,OAAO;AAAA,EAClB;AACF;AAEA,eAAe,oBAAoB,SAAiD;AAClF,QAAM,SAASC,MAAK,SAAS,gBAAgB;AAC7C,QAAM,QAAQ,MAAM,UAAU,MAAM;AACpC,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,CAAC;AACvD,QAAM,SAAuB,CAAC;AAC9B,aAAW,QAAQ,UAAU;AAC3B,WAAO,KAAK,MAAM,YAAYA,MAAK,QAAQ,IAAI,GAAG,IAAI,CAAC;AAAA,EACzD;AACA,SAAO;AACT;AAEA,eAAe,QAAQ,WAAmB,MAAkC;AAC1E,QAAM,UAAUA,MAAK,WAAW,IAAI;AACpC,QAAM,eAAe,MAAM,oBAAoB,OAAO;AACtD,SAAO,EAAE,MAAM,SAAS,MAAM,aAAa;AAC7C;AAEA,eAAe,UAAU,SAAiB,SAAmD;AAC3F,QAAM,OAAO,kBAAkB,SAAS,mBAAmB;AAC3D,MAAI,SAAS,QAAW;AACtB,WAAO;AAAA,EACT;AACA,QAAM,YAAYA,MAAK,SAAS,OAAO;AACvC,QAAM,UAAU,MAAM,YAAY,SAAS;AAC3C,QAAM,OAAoB,CAAC;AAC3B,aAAW,UAAU,SAAS;AAC5B,SAAK,KAAK,MAAM,QAAQ,WAAW,MAAM,CAAC;AAAA,EAC5C;AACA,SAAO,EAAE,MAAM,WAAW,MAAM,KAAK;AACvC;AAEA,eAAe,QAAQ,YAAoB,SAAiD;AAC1F,QAAM,OAAO,kBAAkB,SAAS,iBAAiB;AACzD,MAAI,SAAS,QAAW;AACtB,WAAO;AAAA,EACT;AACA,QAAM,UAAUA,MAAK,YAAY,OAAO;AACxC,QAAM,YAAY,MAAM,YAAY,OAAO;AAC3C,QAAM,SAAwB,CAAC;AAC/B,aAAW,YAAY,WAAW;AAChC,UAAM,QAAQ,MAAM,UAAU,SAAS,QAAQ;AAC/C,QAAI,OAAO;AACT,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AACA,SAAO,EAAE,MAAM,SAAS,MAAM,OAAO;AACvC;AAEA,eAAe,WAAW,MAAc,SAAoD;AAC1F,QAAM,MAAM,kBAAkB,SAAS,oBAAoB;AAC3D,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,QAAM,aAAaA,MAAK,MAAM,OAAO;AACrC,QAAM,UAAU,MAAM,YAAY,UAAU;AAC5C,QAAM,OAAoB,CAAC;AAC3B,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,MAAM,QAAQ,YAAY,MAAM;AAC5C,QAAI,KAAK;AACP,WAAK,KAAK,GAAG;AAAA,IACf;AAAA,EACF;AACA,SAAO,EAAE,MAAM,YAAY,KAAK,KAAK;AACvC;AAEA,eAAsB,eAAe,MAAwC;AAC3E,QAAM,aAAa,MAAM,YAAY,IAAI;AACzC,QAAM,UAA0B,CAAC;AACjC,aAAW,OAAO,YAAY;AAC5B,UAAM,SAAS,MAAM,WAAW,MAAM,GAAG;AACzC,QAAI,QAAQ;AACV,cAAQ,KAAK,MAAM;AAAA,IACrB;AAAA,EACF;AACA,SAAO,EAAE,MAAM,QAAQ;AACzB;AAWO,SAAS,mBAAmB,WAA6C;AAC9E,QAAM,UAAU,UAAU,QAAQ,UAAU,EAAE,EAAE,QAAQ,OAAO,GAAG;AAClE,QAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC1D,MAAI,KAAK,SAAS,GAAG;AACnB,WAAO;AAAA,EACT;AACA,QAAM,CAAC,QAAQ,KAAK,OAAO,KAAK,GAAG,IAAI,IAAI;AAC3C,MAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK;AACrC,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,EAAE,QAAQ,KAAK,OAAO,IAAI;AAAA,EACnC;AACA,QAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,QAAM,OAAO,KAAK,KAAK,SAAS,CAAC,KAAK;AACtC,QAAM,cAAc,KAAK,SAAS,MAAM,IAAI,KAAK,QAAQ,UAAU,EAAE,IAAI;AACzE,SAAO,cACH,EAAE,QAAQ,KAAK,OAAO,KAAK,aAAa,SAAS,IACjD,EAAE,QAAQ,KAAK,OAAO,KAAK,SAAS;AAC1C;;;ADjGA,IAAMC,WAAU,cAAc,YAAY,GAAG;AAa7C,SAAS,YAAY,KAA2C;AAC9D,QAAM,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK;AACpD,SAAO,MAAM,MAAM,SAAS,EAAE,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAClE;AAEA,SAAS,eAAe,SAAiB,KAA2C;AAClF,MAAI,QAAQ,aAAa,WAAW,QAAQ,SAAS,GAAG,GAAG;AACzD,WAAO,CAAC,OAAO;AAAA,EACjB;AACA,QAAM,UACJ,IAAI,SAAS,GAAG,MAAM,GAAG,EAAE,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC,KAAK,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AACnG,SAAO,CAAC,SAAS,GAAG,QAAQ,IAAI,CAAC,QAAQ,GAAG,OAAO,GAAG,GAAG,EAAE,CAAC;AAC9D;AAEA,eAAe,kBAAkB,SAAiB,KAAqD;AACrG,QAAM,aAAa,eAAe,SAAS,GAAG;AAC9C,aAAW,SAAS,YAAY,GAAG,GAAG;AACpC,eAAW,aAAa,YAAY;AAClC,YAAM,WAAWC,MAAK,OAAO,SAAS;AACtC,UAAI,MAAM,OAAO,QAAQ,GAAG;AAC1B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,mBAAmB,OAAoC;AAC9D,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,OAAO,QAAQ,UAAU;AAC3B,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,GAAG,GAAG;AAClB,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI,KAAK;AACrB,SAAO,OAAO,QAAQ,WAAW,MAAM;AACzC;AAEA,SAAS,gCAAwC;AAC/C,SAAOD,SAAQ,QAAQ,4BAA4B;AACrD;AAEA,eAAe,oBAAoB,MAA+B;AAChE,SAAO,MAAME,UAAS,MAAM,MAAM;AACpC;AAEA,eAAe,yBACb,MAC6B;AAC7B,MAAI;AACF,UAAM,mBAAmB,KAAK,0BAA0B,+BAA+B;AACvF,UAAM,MAAM,OAAO,KAAK,gBAAgB,qBAAqB,eAAe;AAC5E,UAAM,UAAU,mBAAmB,KAAK,MAAM,GAAG,CAAY;AAC7D,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AACA,WAAO,QAAQC,SAAQ,eAAe,GAAG,OAAO;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,kBACpB,MAAyB,QAAQ,KACjC,OAA8B,CAAC,GACV;AACrB,QAAM,SAAS,OAAO,KAAK,cAAc,mBAAmB,OAAO,GAAG;AACtE,MAAI,QAAQ;AACV,WAAO,EAAE,SAAS,QAAQ,YAAY,CAAC,EAAE;AAAA,EAC3C;AACA,QAAM,aAAa,MAAM,yBAAyB,IAAI;AACtD,MAAI,YAAY;AACd,WAAO,EAAE,SAAS,QAAQ,UAAU,YAAY,CAAC,UAAU,EAAE;AAAA,EAC/D;AACA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,gBACb,MACA,KACA,KACyB;AACzB,QAAM,UAAU,MAAM,kBAAkB,GAAG;AAC3C,SAAO,MAAM,IAAI,QAAQ,CAAC,gBAAgB,kBAAkB;AAC1D,UAAM,QAAQ,MAAM,QAAQ,SAAS,CAAC,GAAG,QAAQ,YAAY,GAAG,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO,OAAO,CAAC;AAClG,QAAI,SAAS;AACb,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS,MAAM;AAC/B,cAAQ,OAAO,MAAM,KAAK;AAAA,IAC5B,CAAC;AACD,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS,MAAM;AAC/B,cAAQ,OAAO,MAAM,KAAK;AAAA,IAC5B,CAAC;AACD,UAAM,GAAG,SAAS,aAAa;AAC/B,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,qBAAe,EAAE,MAAM,QAAQ,GAAG,QAAQ,OAAO,CAAC;AAAA,IACpD,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,OAAO,MAAgC;AACpD,MAAI;AACF,UAAM,KAAK,IAAI;AACf,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cACb,MACA,QACsF;AACtF,QAAM,SAAS,WAAW,MAAM,IAAI,SAAS,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAC1E,MAAI,MAAM,OAAO,MAAM,GAAG;AACxB,WAAO,EAAE,UAAU,QAAQ,WAAW,OAAU;AAAA,EAClD;AAEA,QAAM,YAAY,mBAAmB,MAAM;AAC3C,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,qBAAqB,MAAM,EAAE;AAAA,EAC/C;AAEA,QAAM,EAAE,QAAQ,KAAK,OAAO,KAAK,SAAS,IAAI;AAC9C,QAAM,SAASF;AAAA,IACb;AAAA,IACA,iBAAiB,MAAM;AAAA,IACvB,cAAc,GAAG;AAAA,IACjB,gBAAgB,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,WAAO,EAAE,UAAU,QAAQ,UAAU;AAAA,EACvC;AAEA,QAAM,YAAYA,MAAK,QAAQ,QAAQ;AACvC,MAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,WAAO,EAAE,UAAU,WAAW,UAAU;AAAA,EAC1C;AAEA,QAAM,UAAU,UAAU,SAAS,MAAM,IAAI,YAAY,GAAG,SAAS;AACrE,MAAI,MAAM,OAAO,OAAO,GAAG;AACzB,WAAO,EAAE,UAAU,SAAS,UAAU;AAAA,EACxC;AAEA,QAAM,IAAI,MAAM,mBAAmB,SAAS,EAAE;AAChD;AAEA,eAAe,sBACb,QACA,aACqE;AACrE,MAAI,aAAa;AACf,UAAM,UAAUA,MAAK,QAAQ,kBAAkB,GAAG,WAAW,MAAM;AACnE,QAAI,CAAE,MAAM,OAAO,OAAO,GAAI;AAC5B,YAAM,IAAI,MAAM,+BAA+B,OAAO,EAAE;AAAA,IAC1D;AACA,WAAO,EAAE,SAAS,YAAY;AAAA,EAChC;AAEA,QAAM,aAAa,MAAM,eAAe,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI,CAAC;AAC/E,aAAW,UAAU,WAAW,SAAS;AACvC,eAAW,OAAO,OAAO,MAAM;AAC7B,iBAAW,SAAS,IAAI,QAAQ;AAC9B,mBAAW,OAAO,MAAM,MAAM;AAC5B,cAAI,IAAI,SAAS,UAAU,IAAI,aAAa,SAAS,GAAG;AACtD,kBAAM,QAAQ,IAAI,aAAa,CAAC;AAChC,gBAAI,OAAO;AACT,qBAAO,EAAE,SAAS,MAAM,MAAM,aAAa,MAAM,KAAK;AAAA,YACxD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI,MAAM,oCAAoC,MAAM,IAAI,gBAAgB,EAAE;AAClF;AAEA,SAAS,mBAAmB,UAAkB,MAAsB;AAClE,QAAM,MAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,GAAG;AAC9C,MAAI,IAAI,SAAS,GAAG;AAClB,UAAM,IAAI,MAAM,wDAAwD,QAAQ,EAAE;AAAA,EACpF;AACA,QAAM,CAAC,WAAW,QAAQ,UAAU,MAAM,IAAI;AAC9C,MAAI,CAAC,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ;AACjD,UAAM,IAAI,MAAM,wDAAwD,QAAQ,EAAE;AAAA,EACpF;AACA,SAAOA,MAAK,MAAM,WAAW,QAAQ,UAAU,MAAM;AACvD;AAEA,eAAsB,aAAa,SAAuC;AACxE,QAAM,EAAE,SAAS,IAAI,MAAM,cAAc,QAAQ,MAAM,QAAQ,MAAM;AACrE,QAAM,QAAQ,MAAM,KAAK,QAAQ;AAEjC,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM,YAAY,GAAG;AACvB,aAAS;AACT,kBAAc;AAAA,EAChB,OAAO;AACL,aAAS,mBAAmB,UAAU,QAAQ,IAAI;AAClD,kBAAc;AAAA,EAChB;AAEA,QAAM,EAAE,SAAS,YAAY,IAAI,MAAM,sBAAsB,QAAQ,QAAQ,WAAW;AAExF,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAC7C,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR,uDAAuD,OAAO;AAAA,IAChE;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,kBAAkB;AAC3C,QAAM,QAAQ,MAAM,SAAS,IAAI;AAEjC,QAAM,UAAoB,CAAC,KAAK;AAChC,MAAI,aAAa;AACf,YAAQ,KAAK,SAAS,QAAQ,WAAW,KAAK,GAAG;AAAA,EACnD;AACA,UAAQ,KAAK,SAAS,aAAa,aAAa,eAAe,KAAK,EAAE;AACtE,MAAI,QAAQ,WAAW;AACrB,YAAQ,KAAK,GAAG,QAAQ,SAAS;AAAA,EACnC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACP;AACF;AAEA,eAAsB,SAAS,SAAyC;AACtE,QAAM,OAAO,MAAM,aAAa,OAAO;AACvC,QAAM,UAAU,QAAQ,YAAY;AACpC,QAAM,MAAyB,EAAE,GAAG,QAAQ,KAAK,uBAAuB,KAAK,MAAM;AACnF,UAAQ,MAAM,cAAS,KAAK,QAAQ,KAAK,GAAG,CAAC,UAAU,KAAK,GAAG,GAAG;AAClE,QAAM,SAAS,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG;AACxD,SAAO,EAAE,GAAG,MAAM,GAAG,OAAO;AAC9B;;;AEnUO,SAAS,sBAAsB,WAKxB;AACZ,QAAM,OAAO,UAAU,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC5D,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO;AAAA,EACT;AACA,QAAM,CAAC,QAAQ,KAAK,OAAO,GAAG,IAAI;AAClC,MAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK;AACrC,WAAO;AAAA,EACT;AACA,SAAO,EAAE,QAAQ,KAAK,OAAO,IAAI;AACnC;AAQA,eAAsB,WAAW,SAA4C;AAC3E,QAAM,SAAS,sBAAsB,QAAQ,SAAS;AACtD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,8BAA8B,QAAQ,SAAS;AAAA,IACjD;AAAA,EACF;AAEA,MAAI,CAAC,iBAAiB,OAAO,MAAM,GAAG;AACpC,UAAM,IAAI,MAAM,uBAAuB,OAAO,MAAM,EAAE;AAAA,EACxD;AAEA,MAAI,QAAQ,WAAW,OAAO;AAC5B,UAAM,WAAW,MAAM,WAAW,EAAE,GAAG,QAAQ,QAAQ,OAAO,OAAO,GAAG,QAAQ,IAAI;AACpF,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR,oBAAoB,QAAQ,SAAS;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,aAAa,MAAM;AAClC;;;AXvCA,SAAS,qBAAqB,oBAAwC,cAA0C;AAC9G,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AACA,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AACA,MAAIG,SAAQ,IAAI,2BAA2B,GAAG;AAC5C,WAAOA,SAAQ,IAAI,2BAA2B;AAAA,EAChD;AACA,MAAIA,SAAQ,IAAI,qBAAqB,GAAG;AACtC,WAAOA,SAAQ,IAAI,qBAAqB;AAAA,EAC1C;AACA,SAAOA,SAAQ,IAAI;AACrB;AAEA,eAAsB,KAAK,MAAwC;AACjE,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,gBAAgB,EACrB,YAAY,iFAAiF,EAC7F,UAAU,IAAI,OAAO,sBAAsB,wEAAwE,CAAC,EACpH,UAAU,IAAI,OAAO,gBAAgB,+BAA+B,EAAE,SAAS,CAAC;AAEnF,UACG,QAAQ,WAAW,EACnB,YAAY,qEAAqE,EACjF,OAAO,YAA2B;AACjC,UAAM,gBAAgB;AAAA,MACpB,QAAQ,KAA6C,EAAE;AAAA,MACvD,QAAQ,KAA6C,EAAE;AAAA,IACzD;AACA,UAAM,SAAS,MAAM,SAAS;AAAA,MAC5B,MAAM;AAAA,MACN,SAAS;AAAA,QACP,cAAc,OAAO,YAAY,MAAM,OAAO,EAAE,SAAS,iBAAiB,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,QACjG,WAAW,OAAO,YAAY,MAAM,OAAO,EAAE,SAAS,cAAc,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,QAC3F,aAAa,OAAO,YAAY,MAAM,OAAO,EAAE,SAAS,gBAAgB,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,QAC/F,WAAW,OAAO,YAAY,MAAM,OAAO,EAAE,SAAS,cAAc,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,QAC3F,eAAe,OAAO,SAAS,MAAM,QAAQ,EAAE,SAAS,UAAU,IAAI,KAAK,SAAS,KAAK,CAAC;AAAA,QAC1F,oBAAoB,OAAO,SAAS,MAAM,sBAAsB,IAAI;AAAA,MACtE;AAAA,MACA,KAAK,CAAC,QAAQ;AACZ,QAAAA,SAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,MACjC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,OAAO,SAAS;AACnB,MAAAA,SAAQ,OAAO,MAAM,YAAY;AACjC;AAAA,IACF;AACA,IAAAA,SAAQ,OAAO,MAAM,8BAAyB,OAAO,OAAO;AAAA,CAAI;AAAA,EAClE,CAAC;AAEH,UACG,QAAQ,KAAK,EACb,YAAY,8DAA8D,EAC1E,SAAS,YAAY,sEAAsE,EAC3F,OAAO,oBAAoB,8CAA8C,EACzE;AAAA,IACC,OACE,QACA,SACkB;AAClB,YAAM,gBAAgB;AAAA,QACpB,QAAQ,KAA6C,EAAE;AAAA,QACvD,QAAQ,KAA6C,EAAE;AAAA,MACzD;AACA,UAAI,kBAAkB;AAEtB,UAAI,CAAC,iBAAiB;AACpB,cAAM,MAAM,MAAM,YAAY;AAC9B,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,0BAAkB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG;AAAA,MACpE;AAEA,YAAM,SAAS,MAAM,SAAS;AAAA,QAC5B,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,GAAI,KAAK,MAAM,EAAE,aAAa,KAAK,IAAI,IAAI,CAAC;AAAA,QAC5C,KAAK,CAAC,QAAQ;AACZ,UAAAA,SAAQ,OAAO,MAAM,GAAG,GAAG;AAAA,CAAI;AAAA,QACjC;AAAA,MACF,CAAC;AACD,MAAAA,SAAQ,KAAK,OAAO,IAAI;AAAA,IAC1B;AAAA,EACF;AAEF,UACG,QAAQ,KAAK,EACb,YAAY,0EAA0E,EACtF,SAAS,eAAe,sBAAsB,EAC9C,OAAO,eAAe,4DAA4D,EAClF,OAAO,OAAO,WAAmB,SAA8C;AAC9E,UAAM,MAAM,MAAM,WAAW;AAAA,MAC3B;AAAA,MACA,QAAQ,KAAK,WAAW;AAAA,IAC1B,CAAC;AACD,IAAAA,SAAQ,OAAO,MAAM,iCAA4B,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG;AAAA,CAAI;AAAA,EACpG,CAAC;AAEH,QAAM,QAAQ,WAAW,CAAC,GAAG,IAAI,CAAC;AACpC;AAEA,IAAI;AACF,QAAM,KAAKA,SAAQ,IAAI;AACzB,SAAS,KAAc;AACrB,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,EAAAA,SAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AACtC,EAAAA,SAAQ,KAAK,CAAC;AAChB;","names":["process","mkdir","writeFile","join","readFile","writeFile","sep","readFile","writeFile","join","writeFile","mkdir","readFile","dirname","join","readdir","readFile","join","readdir","readFile","join","require","join","readFile","dirname","process"]}
package/dist/index.js CHANGED
@@ -454,8 +454,9 @@ async function writeContext(ctx) {
454
454
 
455
455
  // src/setup-app.ts
456
456
  import { mkdir as mkdir2, readdir as readdir2, writeFile as writeFile3 } from "fs/promises";
457
- import { join as join3 } from "path";
457
+ import { basename, join as join3 } from "path";
458
458
  var COMMON_ENVIRONMENTS = ["local", "dev", "staging", "prod"];
459
+ var BRUNO_COLLECTION_CONFIG_FILENAME = "bruno.json";
459
460
  var ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
460
461
  function assertValidEnvName(name) {
461
462
  if (!ENV_NAME_PATTERN.test(name)) {
@@ -478,6 +479,33 @@ function emptyEnvContent(envName, ref) {
478
479
  ];
479
480
  return lines.join("\n");
480
481
  }
482
+ function normalizeCollectionName(root) {
483
+ const candidate = basename(root).replace(/^\.+/, "").trim();
484
+ return candidate.length > 0 ? candidate : "bruno-collection";
485
+ }
486
+ function defaultBrunoConfig(root) {
487
+ return `${JSON.stringify(
488
+ {
489
+ version: "1",
490
+ name: normalizeCollectionName(root),
491
+ type: "collection",
492
+ ignore: ["node_modules", ".git"]
493
+ },
494
+ null,
495
+ 2
496
+ )}
497
+ `;
498
+ }
499
+ async function ensureCollectionConfig(root) {
500
+ const filePath = join3(root, BRUNO_COLLECTION_CONFIG_FILENAME);
501
+ try {
502
+ await writeFile3(filePath, defaultBrunoConfig(root), { encoding: "utf8", flag: "wx" });
503
+ } catch (err) {
504
+ if (err.code !== "EEXIST") {
505
+ throw err;
506
+ }
507
+ }
508
+ }
481
509
  async function ensureEnvFile(appPath, envName, ref) {
482
510
  const envDir = join3(appPath, ENVIRONMENTS_DIR);
483
511
  await mkdir2(envDir, { recursive: true });
@@ -551,6 +579,8 @@ async function setupApp(options) {
551
579
  if (!confirmed) {
552
580
  return { ref, appPath, environments: [], created: false };
553
581
  }
582
+ await mkdir2(options.root, { recursive: true });
583
+ await ensureCollectionConfig(options.root);
554
584
  await mkdir2(appPath, { recursive: true });
555
585
  const existingEnvs = await listExistingEnvs(appPath);
556
586
  const common = [...COMMON_ENVIRONMENTS];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/paths.ts","../src/bru-parser.ts","../src/bru-writer.ts","../src/cf-info.ts","../src/cf-meta.ts","../src/folder-scan.ts","../src/context.ts","../src/setup-app.ts","../src/run.ts","../src/use.ts"],"sourcesContent":["import type { RegionKey } from \"@saptools/cf-sync\";\n\nexport interface CfAppRef {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport interface BruVarsBlock {\n readonly entries: ReadonlyMap<string, string>;\n}\n\nexport interface BruEnvFile {\n readonly path: string;\n readonly name: string;\n readonly raw: string;\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport interface AppFolder {\n readonly path: string;\n readonly name: string;\n readonly environments: readonly BruEnvFile[];\n}\n\nexport interface SpaceFolder {\n readonly path: string;\n readonly name: string;\n readonly apps: readonly AppFolder[];\n}\n\nexport interface OrgFolder {\n readonly path: string;\n readonly name: string;\n readonly spaces: readonly SpaceFolder[];\n}\n\nexport interface RegionFolder {\n readonly path: string;\n readonly key: string;\n readonly orgs: readonly OrgFolder[];\n}\n\nexport interface BrunoCollection {\n readonly root: string;\n readonly regions: readonly RegionFolder[];\n}\n\nexport interface BrunoContext {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly updatedAt: string;\n}\n\nexport interface CfRoute {\n readonly url: string;\n}\n\nexport const CF_META_KEYS = [\"__cf_region\", \"__cf_org\", \"__cf_space\", \"__cf_app\"] as const;\nexport type CfMetaKey = (typeof CF_META_KEYS)[number];\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const BRUNO_CONTEXT_FILENAME = \"bruno-context.json\";\n\nexport const REGION_FOLDER_PREFIX = \"region__\";\nexport const ORG_FOLDER_PREFIX = \"org__\";\nexport const SPACE_FOLDER_PREFIX = \"space__\";\nexport const ENVIRONMENTS_DIR = \"environments\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function brunoContextPath(): string {\n return join(saptoolsDir(), BRUNO_CONTEXT_FILENAME);\n}\n\nexport function regionFolderName(key: string): string {\n return `${REGION_FOLDER_PREFIX}${key}`;\n}\n\nexport function orgFolderName(name: string): string {\n return `${ORG_FOLDER_PREFIX}${name}`;\n}\n\nexport function spaceFolderName(name: string): string {\n return `${SPACE_FOLDER_PREFIX}${name}`;\n}\n\nexport function parsePrefixedName(\n dirName: string,\n prefix: string,\n): string | undefined {\n if (!dirName.startsWith(prefix)) {\n return undefined;\n }\n return dirName.slice(prefix.length);\n}\n","import type { BruVarsBlock } from \"./types.js\";\n\ninterface BlockRange {\n readonly header: string;\n readonly start: number;\n readonly end: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n readonly open: \"{\" | \"[\";\n readonly close: \"}\" | \"]\";\n}\n\nconst HEADER_REGEX = /(^|\\n)\\s*([a-zA-Z][a-zA-Z0-9:_-]*)\\s*([{[])/g;\n\nfunction findMatchingClose(raw: string, open: \"{\" | \"[\", openIdx: number): number {\n const close = open === \"{\" ? \"}\" : \"]\";\n let depth = 1;\n let i = openIdx + 1;\n while (i < raw.length) {\n const ch = raw[i];\n if (ch === open) {\n depth++;\n } else if (ch === close) {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n i++;\n }\n return -1;\n}\n\nexport function listBlocks(raw: string): readonly BlockRange[] {\n const blocks: BlockRange[] = [];\n HEADER_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = HEADER_REGEX.exec(raw)) !== null) {\n const leadingNewline = match[1] ?? \"\";\n const header = match[2];\n const open = match[3];\n if (header === undefined || (open !== \"{\" && open !== \"[\")) {\n continue;\n }\n const headerStart = match.index + leadingNewline.length;\n const openIdx = match.index + match[0].length - 1;\n const closeIdx = findMatchingClose(raw, open, openIdx);\n if (closeIdx === -1) {\n continue;\n }\n blocks.push({\n header,\n start: headerStart,\n end: closeIdx + 1,\n bodyStart: openIdx + 1,\n bodyEnd: closeIdx,\n open,\n close: open === \"{\" ? \"}\" : \"]\",\n });\n }\n return blocks;\n}\n\nexport function parseKeyValueBody(body: string): Map<string, string> {\n const entries = new Map<string, string>();\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n continue;\n }\n const key = line.slice(0, colon).trim();\n const value = line.slice(colon + 1).trim();\n if (key.length > 0) {\n entries.set(key, value);\n }\n }\n return entries;\n}\n\nexport function parseListBody(body: string): string[] {\n const items: string[] = [];\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n items.push(line);\n }\n return items;\n}\n\nexport interface ParsedBruEnv {\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport function parseBruEnvFile(raw: string): ParsedBruEnv {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n const entries = varsBlock\n ? parseKeyValueBody(raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd))\n : new Map<string, string>();\n const secrets = secretsBlock\n ? parseListBody(raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd))\n : [];\n\n return { vars: { entries }, secrets };\n}\n","import { listBlocks, parseKeyValueBody } from \"./bru-parser.js\";\n\nexport interface UpsertResult {\n readonly content: string;\n readonly changed: boolean;\n}\n\nfunction formatVarsBlock(entries: ReadonlyMap<string, string>): string {\n const lines: string[] = [];\n for (const [key, value] of entries) {\n lines.push(` ${key}: ${value}`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function upsertVars(\n raw: string,\n updates: ReadonlyMap<string, string>,\n): UpsertResult {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n\n if (!varsBlock) {\n const newBlock = `vars {\\n${formatVarsBlock(updates)}\\n}\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: updates.size > 0 };\n }\n\n const body = raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd);\n const existing = parseKeyValueBody(body);\n let changed = false;\n for (const [k, v] of updates) {\n if (existing.get(k) !== v) {\n existing.set(k, v);\n changed = true;\n }\n }\n\n if (!changed) {\n return { content: raw, changed: false };\n }\n\n const rebuilt = `\\n${formatVarsBlock(existing)}\\n`;\n const before = raw.slice(0, varsBlock.bodyStart);\n const after = raw.slice(varsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n\nexport function ensureSecretEntry(raw: string, secretName: string): UpsertResult {\n const blocks = listBlocks(raw);\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n if (!secretsBlock) {\n const newBlock = `vars:secret [\\n ${secretName}\\n]\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: true };\n }\n\n const body = raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd);\n const items = body\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0 && !l.startsWith(\"//\"));\n if (items.includes(secretName)) {\n return { content: raw, changed: false };\n }\n items.push(secretName);\n const rebuilt = `\\n ${items.join(\"\\n \")}\\n`;\n const before = raw.slice(0, secretsBlock.bodyStart);\n const after = raw.slice(secretsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n","import type {\n CfStructure,\n OrgNode,\n RegionKey,\n RegionNode,\n RegionsView,\n RegionView,\n SpaceNode,\n StructureView,\n} from \"@saptools/cf-sync\";\nimport {\n getRegionView as getRegionViewApi,\n readRegionsView,\n readRegionView,\n readStructureView,\n REGION_KEYS,\n} from \"@saptools/cf-sync\";\n\nexport interface CfInfoDeps {\n readonly readStructureView: () => Promise<StructureView | undefined>;\n readonly readRegionsView: () => Promise<RegionsView>;\n readonly readRegionView: (key: RegionKey) => Promise<RegionView | undefined>;\n readonly getRegionView: (opts: {\n readonly regionKey: RegionKey;\n readonly email?: string;\n readonly password?: string;\n readonly refreshIfMissing?: boolean;\n }) => Promise<RegionView | undefined>;\n}\n\nexport const defaultCfInfoDeps: CfInfoDeps = {\n readStructureView,\n readRegionsView,\n readRegionView,\n getRegionView: getRegionViewApi,\n};\n\nexport function isValidRegionKey(value: string): value is RegionKey {\n return (REGION_KEYS as readonly string[]).includes(value);\n}\n\nexport interface StructureSnapshot {\n readonly source: \"runtime\" | \"stable\" | \"empty\";\n readonly structure: CfStructure | undefined;\n readonly stale: boolean;\n readonly message: string | undefined;\n}\n\nexport async function getStructureSnapshot(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<StructureSnapshot> {\n const view = await deps.readStructureView();\n if (!view) {\n return {\n source: \"empty\",\n structure: undefined,\n stale: true,\n message: \"No CF structure cached. Run `cf-sync sync` first.\",\n };\n }\n\n const stale = view.source === \"runtime\" && view.metadata?.status === \"running\";\n return {\n source: view.source,\n structure: view.structure,\n stale,\n message: stale ? \"A CF sync is still running โ€” showing partial data.\" : undefined,\n };\n}\n\nexport interface RegionSuggestion {\n readonly key: RegionKey;\n readonly label: string;\n readonly orgCount: number;\n}\n\nexport async function listRegionsWithContent(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<readonly RegionSuggestion[]> {\n const snapshot = await getStructureSnapshot(deps);\n if (!snapshot.structure) {\n return [];\n }\n return snapshot.structure.regions\n .filter((r) => r.accessible && r.orgs.length > 0)\n .map((r) => ({ key: r.key, label: r.label, orgCount: r.orgs.length }));\n}\n\nexport async function getRegion(\n key: RegionKey,\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<RegionNode | undefined> {\n const view = await deps.readRegionView(key);\n return view?.region;\n}\n\nexport function findOrg(region: RegionNode, orgName: string): OrgNode | undefined {\n return region.orgs.find((o) => o.name === orgName);\n}\n\nexport function findSpace(org: OrgNode, spaceName: string): SpaceNode | undefined {\n return org.spaces.find((s) => s.name === spaceName);\n}\n\nexport function findApp(space: SpaceNode, appName: string): { readonly name: string } | undefined {\n return space.apps.find((a) => a.name === appName);\n}\n\nexport interface ResolvedRef {\n readonly region: RegionNode;\n readonly org: OrgNode;\n readonly space: SpaceNode;\n readonly app: { readonly name: string };\n}\n\nexport async function resolveRef(\n ref: {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n },\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<ResolvedRef | undefined> {\n const region = await getRegion(ref.region, deps);\n if (!region) {\n return undefined;\n }\n const org = findOrg(region, ref.org);\n if (!org) {\n return undefined;\n }\n const space = findSpace(org, ref.space);\n if (!space) {\n return undefined;\n }\n const app = findApp(space, ref.app);\n if (!app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport { upsertVars } from \"./bru-writer.js\";\nimport { CF_META_KEYS } from \"./types.js\";\nimport type { CfAppRef, CfMetaKey } from \"./types.js\";\n\nexport interface CfMeta {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport function readCfMetaFromVars(vars: ReadonlyMap<string, string>): CfMeta | undefined {\n const region = vars.get(\"__cf_region\");\n const org = vars.get(\"__cf_org\");\n const space = vars.get(\"__cf_space\");\n const app = vars.get(\"__cf_app\");\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport function buildCfMetaUpdates(ref: CfAppRef, baseUrl?: string): Map<string, string> {\n const updates = new Map<string, string>();\n const pairs: readonly [CfMetaKey, string][] = [\n [\"__cf_region\", ref.region],\n [\"__cf_org\", ref.org],\n [\"__cf_space\", ref.space],\n [\"__cf_app\", ref.app],\n ];\n for (const [k, v] of pairs) {\n updates.set(k, v);\n }\n if (baseUrl !== undefined) {\n updates.set(\"baseUrl\", baseUrl);\n }\n return updates;\n}\n\nexport function hasCfMeta(vars: ReadonlyMap<string, string>): boolean {\n return CF_META_KEYS.every((k) => {\n const v = vars.get(k);\n return v !== undefined && v.length > 0;\n });\n}\n\nexport async function readCfMetaFromFile(path: string): Promise<CfMeta | undefined> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return readCfMetaFromVars(parsed.vars.entries);\n}\n\nexport async function writeCfMetaToFile(\n path: string,\n ref: CfAppRef,\n baseUrl?: string,\n): Promise<boolean> {\n const raw = await readFile(path, \"utf8\");\n const updates = buildCfMetaUpdates(ref, baseUrl);\n const { content, changed } = upsertVars(raw, updates);\n if (changed) {\n await writeFile(path, content, \"utf8\");\n }\n return changed;\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport {\n ENVIRONMENTS_DIR,\n ORG_FOLDER_PREFIX,\n parsePrefixedName,\n REGION_FOLDER_PREFIX,\n SPACE_FOLDER_PREFIX,\n} from \"./paths.js\";\nimport type {\n AppFolder,\n BrunoCollection,\n BruEnvFile,\n OrgFolder,\n RegionFolder,\n SpaceFolder,\n} from \"./types.js\";\n\nasync function safeReaddir(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function listFiles(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function loadEnvFile(path: string, name: string): Promise<BruEnvFile> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return {\n path,\n name: name.replace(/\\.bru$/, \"\"),\n raw,\n vars: parsed.vars,\n secrets: parsed.secrets,\n };\n}\n\nasync function scanAppEnvironments(appPath: string): Promise<readonly BruEnvFile[]> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n const files = await listFiles(envDir);\n const bruFiles = files.filter((f) => f.endsWith(\".bru\"));\n const loaded: BruEnvFile[] = [];\n for (const file of bruFiles) {\n loaded.push(await loadEnvFile(join(envDir, file), file));\n }\n return loaded;\n}\n\nasync function scanApp(spacePath: string, name: string): Promise<AppFolder> {\n const appPath = join(spacePath, name);\n const environments = await scanAppEnvironments(appPath);\n return { path: appPath, name, environments };\n}\n\nasync function scanSpace(orgPath: string, dirName: string): Promise<SpaceFolder | undefined> {\n const name = parsePrefixedName(dirName, SPACE_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const spacePath = join(orgPath, dirName);\n const appDirs = await safeReaddir(spacePath);\n const apps: AppFolder[] = [];\n for (const appDir of appDirs) {\n apps.push(await scanApp(spacePath, appDir));\n }\n return { path: spacePath, name, apps };\n}\n\nasync function scanOrg(regionPath: string, dirName: string): Promise<OrgFolder | undefined> {\n const name = parsePrefixedName(dirName, ORG_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const orgPath = join(regionPath, dirName);\n const spaceDirs = await safeReaddir(orgPath);\n const spaces: SpaceFolder[] = [];\n for (const spaceDir of spaceDirs) {\n const space = await scanSpace(orgPath, spaceDir);\n if (space) {\n spaces.push(space);\n }\n }\n return { path: orgPath, name, spaces };\n}\n\nasync function scanRegion(root: string, dirName: string): Promise<RegionFolder | undefined> {\n const key = parsePrefixedName(dirName, REGION_FOLDER_PREFIX);\n if (key === undefined) {\n return undefined;\n }\n const regionPath = join(root, dirName);\n const orgDirs = await safeReaddir(regionPath);\n const orgs: OrgFolder[] = [];\n for (const orgDir of orgDirs) {\n const org = await scanOrg(regionPath, orgDir);\n if (org) {\n orgs.push(org);\n }\n }\n return { path: regionPath, key, orgs };\n}\n\nexport async function scanCollection(root: string): Promise<BrunoCollection> {\n const regionDirs = await safeReaddir(root);\n const regions: RegionFolder[] = [];\n for (const dir of regionDirs) {\n const region = await scanRegion(root, dir);\n if (region) {\n regions.push(region);\n }\n }\n return { root, regions };\n}\n\nexport interface ShorthandRef {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly environment?: string;\n readonly filePath?: string;\n}\n\nexport function parseShorthandPath(shorthand: string): ShorthandRef | undefined {\n const cleaned = shorthand.replace(/^[./]+/, \"\").replace(/\\\\/g, \"/\");\n const segs = cleaned.split(\"/\").filter((s) => s.length > 0);\n if (segs.length < 4) {\n return undefined;\n }\n const [region, org, space, app, ...rest] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n if (rest.length === 0) {\n return { region, org, space, app };\n }\n const filePath = rest.join(\"/\");\n const last = rest[rest.length - 1] ?? \"\";\n const environment = last.endsWith(\".bru\") ? last.replace(/\\.bru$/, \"\") : undefined;\n return environment\n ? { region, org, space, app, environment, filePath }\n : { region, org, space, app, filePath };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { brunoContextPath } from \"./paths.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport async function readContext(): Promise<BrunoContext | undefined> {\n try {\n const raw = await readFile(brunoContextPath(), \"utf8\");\n return JSON.parse(raw) as BrunoContext;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n}\n\nexport async function writeContext(ctx: Omit<BrunoContext, \"updatedAt\">): Promise<BrunoContext> {\n const updated: BrunoContext = { ...ctx, updatedAt: new Date().toISOString() };\n const path = brunoContextPath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(updated, null, 2)}\\n`, \"utf8\");\n return updated;\n}\n","import { mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport type { OrgNode, RegionKey, RegionNode, SpaceNode } from \"@saptools/cf-sync\";\n\nimport type { CfInfoDeps } from \"./cf-info.js\";\nimport { defaultCfInfoDeps, listRegionsWithContent } from \"./cf-info.js\";\nimport { writeCfMetaToFile } from \"./cf-meta.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\nimport type { CfAppRef } from \"./types.js\";\n\nexport interface EnvironmentSelection {\n readonly common: readonly string[];\n readonly existing: readonly string[];\n}\n\nexport interface SetupAppPrompts {\n readonly selectRegion: (choices: readonly { value: RegionKey; name: string }[]) => Promise<RegionKey>;\n readonly selectOrg: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectSpace: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectApp: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly confirmCreate: (path: string) => Promise<boolean>;\n readonly selectEnvironments: (opts: EnvironmentSelection) => Promise<readonly string[]>;\n}\n\nexport interface SetupAppOptions {\n readonly root: string;\n readonly prompts: SetupAppPrompts;\n readonly deps?: CfInfoDeps;\n readonly log?: (msg: string) => void;\n}\n\nexport interface SetupAppResult {\n readonly ref: CfAppRef;\n readonly appPath: string;\n readonly environments: readonly string[];\n readonly created: boolean;\n}\n\nexport const COMMON_ENVIRONMENTS = [\"local\", \"dev\", \"staging\", \"prod\"] as const;\n\nexport const ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;\n\nexport function assertValidEnvName(name: string): void {\n if (!ENV_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid environment name '${name}': only letters, digits, dot, underscore, and dash are allowed.`,\n );\n }\n}\n\nfunction emptyEnvContent(envName: string, ref: CfAppRef): string {\n const lines = [\n \"vars {\",\n ` __cf_region: ${ref.region}`,\n ` __cf_org: ${ref.org}`,\n ` __cf_space: ${ref.space}`,\n ` __cf_app: ${ref.app}`,\n ` environment: ${envName}`,\n \" baseUrl: \",\n \"}\",\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\nasync function ensureEnvFile(appPath: string, envName: string, ref: CfAppRef): Promise<string> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n await mkdir(envDir, { recursive: true });\n const filePath = join(envDir, `${envName}.bru`);\n try {\n await writeFile(filePath, emptyEnvContent(envName, ref), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n await writeCfMetaToFile(filePath, ref);\n }\n return filePath;\n}\n\nfunction pickRegion(regions: readonly { key: RegionKey; label: string; orgCount: number }[]): readonly {\n readonly value: RegionKey;\n readonly name: string;\n}[] {\n return regions.map((r) => ({ value: r.key, name: `${r.key} โ€” ${r.label} (${r.orgCount.toString()} org${r.orgCount === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickOrg(region: RegionNode): readonly { value: string; name: string }[] {\n return region.orgs.map((o) => ({ value: o.name, name: `${o.name} (${o.spaces.length.toString()} space${o.spaces.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickSpace(org: OrgNode): readonly { value: string; name: string }[] {\n return org.spaces.map((s) => ({ value: s.name, name: `${s.name} (${s.apps.length.toString()} app${s.apps.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickApp(space: SpaceNode): readonly { value: string; name: string }[] {\n return space.apps.map((a) => ({ value: a.name, name: a.name }));\n}\n\nexport async function setupApp(options: SetupAppOptions): Promise<SetupAppResult> {\n const deps = options.deps ?? defaultCfInfoDeps;\n const log = options.log ?? ((): void => undefined);\n\n const regions = await listRegionsWithContent(deps);\n if (regions.length === 0) {\n throw new Error(\n \"No CF regions with orgs are cached. Run `cf-sync sync` first, or pass SAP_EMAIL/SAP_PASSWORD to refresh.\",\n );\n }\n\n const regionKey = await options.prompts.selectRegion(pickRegion(regions));\n const regionView = await deps.readRegionView(regionKey);\n if (!regionView) {\n throw new Error(`Region ${regionKey} is not cached. Run \\`cf-sync sync\\` or \\`cf-sync region ${regionKey}\\`.`);\n }\n const region = regionView.region;\n\n if (region.orgs.length === 0) {\n throw new Error(`Region ${regionKey} has no accessible orgs.`);\n }\n\n const orgName = await options.prompts.selectOrg(pickOrg(region));\n const org = region.orgs.find((o) => o.name === orgName);\n if (!org) {\n throw new Error(`Org ${orgName} not found in region ${regionKey}`);\n }\n if (org.spaces.length === 0) {\n throw new Error(`Org ${orgName} has no spaces.`);\n }\n\n const spaceName = await options.prompts.selectSpace(pickSpace(org));\n const space = org.spaces.find((s) => s.name === spaceName);\n if (!space) {\n throw new Error(`Space ${spaceName} not found in org ${orgName}`);\n }\n if (space.apps.length === 0) {\n throw new Error(`Space ${spaceName} has no apps.`);\n }\n\n const appName = await options.prompts.selectApp(pickApp(space));\n const ref: CfAppRef = { region: regionKey, org: orgName, space: spaceName, app: appName };\n\n const appPath = join(\n options.root,\n regionFolderName(regionKey),\n orgFolderName(orgName),\n spaceFolderName(spaceName),\n appName,\n );\n\n const confirmed = await options.prompts.confirmCreate(appPath);\n if (!confirmed) {\n return { ref, appPath, environments: [], created: false };\n }\n\n await mkdir(appPath, { recursive: true });\n\n const existingEnvs = await listExistingEnvs(appPath);\n const common = [...COMMON_ENVIRONMENTS];\n const selected = await options.prompts.selectEnvironments({ common, existing: existingEnvs });\n const merged: string[] = [];\n for (const name of selected) {\n const trimmed = name.trim();\n if (trimmed.length === 0 || merged.includes(trimmed)) {\n continue;\n }\n assertValidEnvName(trimmed);\n merged.push(trimmed);\n }\n if (merged.length === 0) {\n throw new Error(\"At least one environment is required.\");\n }\n\n const created: string[] = [];\n for (const envName of merged) {\n const path = await ensureEnvFile(appPath, envName, ref);\n created.push(path);\n log(`โ€ข ${path}`);\n }\n\n return { ref, appPath, environments: created, created: true };\n}\n\nasync function listExistingEnvs(appPath: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(join(appPath, ENVIRONMENTS_DIR), { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith(\".bru\"))\n .map((e) => e.name.replace(/\\.bru$/, \"\"));\n } catch {\n return [];\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\nimport type { AppRef } from \"@saptools/cf-xsuaa\";\nimport { getTokenCached as getTokenCachedApi } from \"@saptools/cf-xsuaa\";\n\nimport { readCfMetaFromFile } from \"./cf-meta.js\";\nimport type { ShorthandRef } from \"./folder-scan.js\";\nimport { parseShorthandPath, scanCollection } from \"./folder-scan.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\n\nexport type GetTokenCachedFn = (ref: AppRef) => Promise<string>;\n\nexport interface RunSpawnResult {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport type SpawnBruFn = (\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n) => Promise<RunSpawnResult>;\n\nexport interface RunOptions {\n readonly root: string;\n readonly target: string;\n readonly environment?: string;\n readonly extraArgs?: readonly string[];\n readonly getTokenCached?: GetTokenCachedFn;\n readonly spawnBru?: SpawnBruFn;\n readonly log?: (msg: string) => void;\n}\n\nexport interface RunPlan {\n readonly filePath: string;\n readonly environment: string;\n readonly envFile: string;\n readonly meta: { readonly region: string; readonly org: string; readonly space: string; readonly app: string };\n readonly token: string;\n readonly bruArgs: readonly string[];\n readonly cwd: string;\n}\n\nexport interface RunResult extends RunPlan {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nconst require = createRequire(import.meta.url);\n\nexport interface BruRuntime {\n readonly command: string;\n readonly argsPrefix: readonly string[];\n}\n\nexport interface ResolveBruRuntimeDeps {\n readonly findOnPath?: (command: string, env: NodeJS.ProcessEnv) => Promise<string | undefined>;\n readonly readTextFile?: (path: string) => Promise<string>;\n readonly resolvePackageJsonPath?: () => string;\n}\n\nfunction pathEntries(env: NodeJS.ProcessEnv): readonly string[] {\n const value = env[\"PATH\"] ?? process.env[\"PATH\"] ?? \"\";\n return value.split(delimiter).filter((entry) => entry.length > 0);\n}\n\nfunction pathCandidates(command: string, env: NodeJS.ProcessEnv): readonly string[] {\n if (process.platform !== \"win32\" || command.includes(\".\")) {\n return [command];\n }\n const pathExt =\n env[\"PATHEXT\"]?.split(\";\").filter((entry) => entry.length > 0) ?? [\".COM\", \".EXE\", \".BAT\", \".CMD\"];\n return [command, ...pathExt.map((ext) => `${command}${ext}`)];\n}\n\nasync function findCommandOnPath(command: string, env: NodeJS.ProcessEnv): Promise<string | undefined> {\n const candidates = pathCandidates(command, env);\n for (const entry of pathEntries(env)) {\n for (const candidate of candidates) {\n const fullPath = join(entry, candidate);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction bruBinRelativePath(value: unknown): string | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const bin = value[\"bin\"];\n if (typeof bin === \"string\") {\n return bin;\n }\n if (!isRecord(bin)) {\n return undefined;\n }\n const bru = bin[\"bru\"];\n return typeof bru === \"string\" ? bru : undefined;\n}\n\nfunction defaultResolvePackageJsonPath(): string {\n return require.resolve(\"@usebruno/cli/package.json\");\n}\n\nasync function defaultReadTextFile(path: string): Promise<string> {\n return await readFile(path, \"utf8\");\n}\n\nasync function resolveBundledBruBinPath(\n deps: ResolveBruRuntimeDeps,\n): Promise<string | undefined> {\n try {\n const packageJsonPath = (deps.resolvePackageJsonPath ?? defaultResolvePackageJsonPath)();\n const raw = await (deps.readTextFile ?? defaultReadTextFile)(packageJsonPath);\n const binPath = bruBinRelativePath(JSON.parse(raw) as unknown);\n if (!binPath) {\n return undefined;\n }\n return resolve(dirname(packageJsonPath), binPath);\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveBruRuntime(\n env: NodeJS.ProcessEnv = process.env,\n deps: ResolveBruRuntimeDeps = {},\n): Promise<BruRuntime> {\n const onPath = await (deps.findOnPath ?? findCommandOnPath)(\"bru\", env);\n if (onPath) {\n return { command: onPath, argsPrefix: [] };\n }\n const bundledBin = await resolveBundledBruBinPath(deps);\n if (bundledBin) {\n return { command: process.execPath, argsPrefix: [bundledBin] };\n }\n throw new Error(\n \"Unable to find Bruno CLI. Install @usebruno/cli or ensure `bru` is available on PATH.\",\n );\n}\n\nasync function defaultSpawnBru(\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n): Promise<RunSpawnResult> {\n const runtime = await resolveBruRuntime(env);\n return await new Promise((resolvePromise, rejectPromise) => {\n const child = spawn(runtime.command, [...runtime.argsPrefix, ...args], { cwd, env, stdio: \"pipe\" });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf8\");\n process.stdout.write(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n process.stderr.write(chunk);\n });\n child.on(\"error\", rejectPromise);\n child.on(\"close\", (code) => {\n resolvePromise({ code: code ?? 0, stdout, stderr });\n });\n });\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveTarget(\n root: string,\n target: string,\n): Promise<{ readonly filePath: string; readonly shorthand: ShorthandRef | undefined }> {\n const direct = isAbsolute(target) ? target : resolve(process.cwd(), target);\n if (await exists(direct)) {\n return { filePath: direct, shorthand: undefined };\n }\n\n const shorthand = parseShorthandPath(target);\n if (!shorthand) {\n throw new Error(`Target not found: ${target}`);\n }\n\n const { region, org, space, app, filePath } = shorthand;\n const appDir = join(\n root,\n regionFolderName(region),\n orgFolderName(org),\n spaceFolderName(space),\n app,\n );\n\n if (!filePath) {\n return { filePath: appDir, shorthand };\n }\n\n const candidate = join(appDir, filePath);\n if (await exists(candidate)) {\n return { filePath: candidate, shorthand };\n }\n\n const withExt = candidate.endsWith(\".bru\") ? candidate : `${candidate}.bru`;\n if (await exists(withExt)) {\n return { filePath: withExt, shorthand };\n }\n\n throw new Error(`File not found: ${candidate}`);\n}\n\nasync function chooseEnvironmentFile(\n appDir: string,\n environment: string | undefined,\n): Promise<{ readonly envFile: string; readonly environment: string }> {\n if (environment) {\n const envFile = join(appDir, ENVIRONMENTS_DIR, `${environment}.bru`);\n if (!(await exists(envFile))) {\n throw new Error(`Environment file not found: ${envFile}`);\n }\n return { envFile, environment };\n }\n\n const collection = await scanCollection(resolve(appDir, \"..\", \"..\", \"..\", \"..\"));\n for (const region of collection.regions) {\n for (const org of region.orgs) {\n for (const space of org.spaces) {\n for (const app of space.apps) {\n if (app.path === appDir && app.environments.length > 0) {\n const first = app.environments[0];\n if (first) {\n return { envFile: first.path, environment: first.name };\n }\n }\n }\n }\n }\n }\n throw new Error(`No environment files found under ${appDir}/${ENVIRONMENTS_DIR}`);\n}\n\nfunction findAppDirFromFile(filePath: string, root: string): string {\n const rel = relative(root, filePath).split(sep);\n if (rel.length < 4) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n const [regionDir, orgDir, spaceDir, appDir] = rel;\n if (!regionDir || !orgDir || !spaceDir || !appDir) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n return join(root, regionDir, orgDir, spaceDir, appDir);\n}\n\nexport async function buildRunPlan(options: RunOptions): Promise<RunPlan> {\n const { filePath } = await resolveTarget(options.root, options.target);\n const stats = await stat(filePath);\n\n let appDir: string;\n let requestFile: string | undefined;\n\n if (stats.isDirectory()) {\n appDir = filePath;\n requestFile = undefined;\n } else {\n appDir = findAppDirFromFile(filePath, options.root);\n requestFile = filePath;\n }\n\n const { envFile, environment } = await chooseEnvironmentFile(appDir, options.environment);\n\n const meta = await readCfMetaFromFile(envFile);\n if (!meta) {\n throw new Error(\n `Missing __cf_region/__cf_org/__cf_space/__cf_app in ${envFile}. Run \\`saptools-bruno setup-app\\` first.`,\n );\n }\n\n const getToken = options.getTokenCached ?? getTokenCachedApi;\n const token = await getToken(meta);\n\n const bruArgs: string[] = [\"run\"];\n if (requestFile) {\n bruArgs.push(relative(appDir, requestFile) || \".\");\n }\n bruArgs.push(\"--env\", environment, \"--env-var\", `accessToken=${token}`);\n if (options.extraArgs) {\n bruArgs.push(...options.extraArgs);\n }\n\n return {\n filePath,\n environment,\n envFile,\n meta,\n token,\n bruArgs,\n cwd: appDir,\n };\n}\n\nexport async function runBruno(options: RunOptions): Promise<RunResult> {\n const plan = await buildRunPlan(options);\n const spawnFn = options.spawnBru ?? defaultSpawnBru;\n const env: NodeJS.ProcessEnv = { ...process.env, SAPTOOLS_ACCESS_TOKEN: plan.token };\n options.log?.(`โ–ถ bru ${plan.bruArgs.join(\" \")} (cwd=${plan.cwd})`);\n const result = await spawnFn(plan.bruArgs, env, plan.cwd);\n return { ...plan, ...result };\n}\n","import type { CfInfoDeps } from \"./cf-info.js\";\nimport { isValidRegionKey, resolveRef } from \"./cf-info.js\";\nimport { writeContext } from \"./context.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport function parseContextShorthand(shorthand: string): {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n} | undefined {\n const segs = shorthand.split(\"/\").filter((s) => s.length > 0);\n if (segs.length !== 4) {\n return undefined;\n }\n const [region, org, space, app] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport interface UseOptions {\n readonly shorthand: string;\n readonly deps?: CfInfoDeps;\n readonly verify?: boolean;\n}\n\nexport async function useContext(options: UseOptions): Promise<BrunoContext> {\n const parsed = parseContextShorthand(options.shorthand);\n if (!parsed) {\n throw new Error(\n `Invalid context shorthand: ${options.shorthand}. Expected <region>/<org>/<space>/<app>.`,\n );\n }\n\n if (!isValidRegionKey(parsed.region)) {\n throw new Error(`Unknown region key: ${parsed.region}`);\n }\n\n if (options.verify !== false) {\n const resolved = await resolveRef({ ...parsed, region: parsed.region }, options.deps);\n if (!resolved) {\n throw new Error(\n `Could not verify ${options.shorthand} against the cached CF structure. Run \\`cf-sync sync\\` first.`,\n );\n }\n }\n\n return await writeContext(parsed);\n}\n"],"mappings":"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","names":["sep","readFile","join","readFile","join","readFile","writeFile","readFile","writeFile","mkdir","readdir","writeFile","join","join","mkdir","writeFile","readdir","readFile","dirname","join","require","join","readFile","dirname"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/paths.ts","../src/bru-parser.ts","../src/bru-writer.ts","../src/cf-info.ts","../src/cf-meta.ts","../src/folder-scan.ts","../src/context.ts","../src/setup-app.ts","../src/run.ts","../src/use.ts"],"sourcesContent":["import type { RegionKey } from \"@saptools/cf-sync\";\n\nexport interface CfAppRef {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport interface BruVarsBlock {\n readonly entries: ReadonlyMap<string, string>;\n}\n\nexport interface BruEnvFile {\n readonly path: string;\n readonly name: string;\n readonly raw: string;\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport interface AppFolder {\n readonly path: string;\n readonly name: string;\n readonly environments: readonly BruEnvFile[];\n}\n\nexport interface SpaceFolder {\n readonly path: string;\n readonly name: string;\n readonly apps: readonly AppFolder[];\n}\n\nexport interface OrgFolder {\n readonly path: string;\n readonly name: string;\n readonly spaces: readonly SpaceFolder[];\n}\n\nexport interface RegionFolder {\n readonly path: string;\n readonly key: string;\n readonly orgs: readonly OrgFolder[];\n}\n\nexport interface BrunoCollection {\n readonly root: string;\n readonly regions: readonly RegionFolder[];\n}\n\nexport interface BrunoContext {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly updatedAt: string;\n}\n\nexport interface CfRoute {\n readonly url: string;\n}\n\nexport const CF_META_KEYS = [\"__cf_region\", \"__cf_org\", \"__cf_space\", \"__cf_app\"] as const;\nexport type CfMetaKey = (typeof CF_META_KEYS)[number];\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const BRUNO_CONTEXT_FILENAME = \"bruno-context.json\";\n\nexport const REGION_FOLDER_PREFIX = \"region__\";\nexport const ORG_FOLDER_PREFIX = \"org__\";\nexport const SPACE_FOLDER_PREFIX = \"space__\";\nexport const ENVIRONMENTS_DIR = \"environments\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function brunoContextPath(): string {\n return join(saptoolsDir(), BRUNO_CONTEXT_FILENAME);\n}\n\nexport function regionFolderName(key: string): string {\n return `${REGION_FOLDER_PREFIX}${key}`;\n}\n\nexport function orgFolderName(name: string): string {\n return `${ORG_FOLDER_PREFIX}${name}`;\n}\n\nexport function spaceFolderName(name: string): string {\n return `${SPACE_FOLDER_PREFIX}${name}`;\n}\n\nexport function parsePrefixedName(\n dirName: string,\n prefix: string,\n): string | undefined {\n if (!dirName.startsWith(prefix)) {\n return undefined;\n }\n return dirName.slice(prefix.length);\n}\n","import type { BruVarsBlock } from \"./types.js\";\n\ninterface BlockRange {\n readonly header: string;\n readonly start: number;\n readonly end: number;\n readonly bodyStart: number;\n readonly bodyEnd: number;\n readonly open: \"{\" | \"[\";\n readonly close: \"}\" | \"]\";\n}\n\nconst HEADER_REGEX = /(^|\\n)\\s*([a-zA-Z][a-zA-Z0-9:_-]*)\\s*([{[])/g;\n\nfunction findMatchingClose(raw: string, open: \"{\" | \"[\", openIdx: number): number {\n const close = open === \"{\" ? \"}\" : \"]\";\n let depth = 1;\n let i = openIdx + 1;\n while (i < raw.length) {\n const ch = raw[i];\n if (ch === open) {\n depth++;\n } else if (ch === close) {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n i++;\n }\n return -1;\n}\n\nexport function listBlocks(raw: string): readonly BlockRange[] {\n const blocks: BlockRange[] = [];\n HEADER_REGEX.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = HEADER_REGEX.exec(raw)) !== null) {\n const leadingNewline = match[1] ?? \"\";\n const header = match[2];\n const open = match[3];\n if (header === undefined || (open !== \"{\" && open !== \"[\")) {\n continue;\n }\n const headerStart = match.index + leadingNewline.length;\n const openIdx = match.index + match[0].length - 1;\n const closeIdx = findMatchingClose(raw, open, openIdx);\n if (closeIdx === -1) {\n continue;\n }\n blocks.push({\n header,\n start: headerStart,\n end: closeIdx + 1,\n bodyStart: openIdx + 1,\n bodyEnd: closeIdx,\n open,\n close: open === \"{\" ? \"}\" : \"]\",\n });\n }\n return blocks;\n}\n\nexport function parseKeyValueBody(body: string): Map<string, string> {\n const entries = new Map<string, string>();\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n continue;\n }\n const key = line.slice(0, colon).trim();\n const value = line.slice(colon + 1).trim();\n if (key.length > 0) {\n entries.set(key, value);\n }\n }\n return entries;\n}\n\nexport function parseListBody(body: string): string[] {\n const items: string[] = [];\n for (const lineRaw of body.split(\"\\n\")) {\n const line = lineRaw.trim();\n if (line.length === 0 || line.startsWith(\"//\")) {\n continue;\n }\n items.push(line);\n }\n return items;\n}\n\nexport interface ParsedBruEnv {\n readonly vars: BruVarsBlock;\n readonly secrets: readonly string[];\n}\n\nexport function parseBruEnvFile(raw: string): ParsedBruEnv {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n const entries = varsBlock\n ? parseKeyValueBody(raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd))\n : new Map<string, string>();\n const secrets = secretsBlock\n ? parseListBody(raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd))\n : [];\n\n return { vars: { entries }, secrets };\n}\n","import { listBlocks, parseKeyValueBody } from \"./bru-parser.js\";\n\nexport interface UpsertResult {\n readonly content: string;\n readonly changed: boolean;\n}\n\nfunction formatVarsBlock(entries: ReadonlyMap<string, string>): string {\n const lines: string[] = [];\n for (const [key, value] of entries) {\n lines.push(` ${key}: ${value}`);\n }\n return lines.join(\"\\n\");\n}\n\nexport function upsertVars(\n raw: string,\n updates: ReadonlyMap<string, string>,\n): UpsertResult {\n const blocks = listBlocks(raw);\n const varsBlock = blocks.find((b) => b.header === \"vars\" && b.open === \"{\");\n\n if (!varsBlock) {\n const newBlock = `vars {\\n${formatVarsBlock(updates)}\\n}\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: updates.size > 0 };\n }\n\n const body = raw.slice(varsBlock.bodyStart, varsBlock.bodyEnd);\n const existing = parseKeyValueBody(body);\n let changed = false;\n for (const [k, v] of updates) {\n if (existing.get(k) !== v) {\n existing.set(k, v);\n changed = true;\n }\n }\n\n if (!changed) {\n return { content: raw, changed: false };\n }\n\n const rebuilt = `\\n${formatVarsBlock(existing)}\\n`;\n const before = raw.slice(0, varsBlock.bodyStart);\n const after = raw.slice(varsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n\nexport function ensureSecretEntry(raw: string, secretName: string): UpsertResult {\n const blocks = listBlocks(raw);\n const secretsBlock = blocks.find((b) => b.header === \"vars:secret\" && b.open === \"[\");\n\n if (!secretsBlock) {\n const newBlock = `vars:secret [\\n ${secretName}\\n]\\n`;\n const sep = raw.length > 0 && !raw.endsWith(\"\\n\") ? \"\\n\\n\" : raw.length > 0 ? \"\\n\" : \"\";\n return { content: `${raw}${sep}${newBlock}`, changed: true };\n }\n\n const body = raw.slice(secretsBlock.bodyStart, secretsBlock.bodyEnd);\n const items = body\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0 && !l.startsWith(\"//\"));\n if (items.includes(secretName)) {\n return { content: raw, changed: false };\n }\n items.push(secretName);\n const rebuilt = `\\n ${items.join(\"\\n \")}\\n`;\n const before = raw.slice(0, secretsBlock.bodyStart);\n const after = raw.slice(secretsBlock.bodyEnd);\n return { content: `${before}${rebuilt}${after}`, changed: true };\n}\n","import type {\n CfStructure,\n OrgNode,\n RegionKey,\n RegionNode,\n RegionsView,\n RegionView,\n SpaceNode,\n StructureView,\n} from \"@saptools/cf-sync\";\nimport {\n getRegionView as getRegionViewApi,\n readRegionsView,\n readRegionView,\n readStructureView,\n REGION_KEYS,\n} from \"@saptools/cf-sync\";\n\nexport interface CfInfoDeps {\n readonly readStructureView: () => Promise<StructureView | undefined>;\n readonly readRegionsView: () => Promise<RegionsView>;\n readonly readRegionView: (key: RegionKey) => Promise<RegionView | undefined>;\n readonly getRegionView: (opts: {\n readonly regionKey: RegionKey;\n readonly email?: string;\n readonly password?: string;\n readonly refreshIfMissing?: boolean;\n }) => Promise<RegionView | undefined>;\n}\n\nexport const defaultCfInfoDeps: CfInfoDeps = {\n readStructureView,\n readRegionsView,\n readRegionView,\n getRegionView: getRegionViewApi,\n};\n\nexport function isValidRegionKey(value: string): value is RegionKey {\n return (REGION_KEYS as readonly string[]).includes(value);\n}\n\nexport interface StructureSnapshot {\n readonly source: \"runtime\" | \"stable\" | \"empty\";\n readonly structure: CfStructure | undefined;\n readonly stale: boolean;\n readonly message: string | undefined;\n}\n\nexport async function getStructureSnapshot(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<StructureSnapshot> {\n const view = await deps.readStructureView();\n if (!view) {\n return {\n source: \"empty\",\n structure: undefined,\n stale: true,\n message: \"No CF structure cached. Run `cf-sync sync` first.\",\n };\n }\n\n const stale = view.source === \"runtime\" && view.metadata?.status === \"running\";\n return {\n source: view.source,\n structure: view.structure,\n stale,\n message: stale ? \"A CF sync is still running โ€” showing partial data.\" : undefined,\n };\n}\n\nexport interface RegionSuggestion {\n readonly key: RegionKey;\n readonly label: string;\n readonly orgCount: number;\n}\n\nexport async function listRegionsWithContent(\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<readonly RegionSuggestion[]> {\n const snapshot = await getStructureSnapshot(deps);\n if (!snapshot.structure) {\n return [];\n }\n return snapshot.structure.regions\n .filter((r) => r.accessible && r.orgs.length > 0)\n .map((r) => ({ key: r.key, label: r.label, orgCount: r.orgs.length }));\n}\n\nexport async function getRegion(\n key: RegionKey,\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<RegionNode | undefined> {\n const view = await deps.readRegionView(key);\n return view?.region;\n}\n\nexport function findOrg(region: RegionNode, orgName: string): OrgNode | undefined {\n return region.orgs.find((o) => o.name === orgName);\n}\n\nexport function findSpace(org: OrgNode, spaceName: string): SpaceNode | undefined {\n return org.spaces.find((s) => s.name === spaceName);\n}\n\nexport function findApp(space: SpaceNode, appName: string): { readonly name: string } | undefined {\n return space.apps.find((a) => a.name === appName);\n}\n\nexport interface ResolvedRef {\n readonly region: RegionNode;\n readonly org: OrgNode;\n readonly space: SpaceNode;\n readonly app: { readonly name: string };\n}\n\nexport async function resolveRef(\n ref: {\n readonly region: RegionKey;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n },\n deps: CfInfoDeps = defaultCfInfoDeps,\n): Promise<ResolvedRef | undefined> {\n const region = await getRegion(ref.region, deps);\n if (!region) {\n return undefined;\n }\n const org = findOrg(region, ref.org);\n if (!org) {\n return undefined;\n }\n const space = findSpace(org, ref.space);\n if (!space) {\n return undefined;\n }\n const app = findApp(space, ref.app);\n if (!app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport { upsertVars } from \"./bru-writer.js\";\nimport { CF_META_KEYS } from \"./types.js\";\nimport type { CfAppRef, CfMetaKey } from \"./types.js\";\n\nexport interface CfMeta {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport function readCfMetaFromVars(vars: ReadonlyMap<string, string>): CfMeta | undefined {\n const region = vars.get(\"__cf_region\");\n const org = vars.get(\"__cf_org\");\n const space = vars.get(\"__cf_space\");\n const app = vars.get(\"__cf_app\");\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport function buildCfMetaUpdates(ref: CfAppRef, baseUrl?: string): Map<string, string> {\n const updates = new Map<string, string>();\n const pairs: readonly [CfMetaKey, string][] = [\n [\"__cf_region\", ref.region],\n [\"__cf_org\", ref.org],\n [\"__cf_space\", ref.space],\n [\"__cf_app\", ref.app],\n ];\n for (const [k, v] of pairs) {\n updates.set(k, v);\n }\n if (baseUrl !== undefined) {\n updates.set(\"baseUrl\", baseUrl);\n }\n return updates;\n}\n\nexport function hasCfMeta(vars: ReadonlyMap<string, string>): boolean {\n return CF_META_KEYS.every((k) => {\n const v = vars.get(k);\n return v !== undefined && v.length > 0;\n });\n}\n\nexport async function readCfMetaFromFile(path: string): Promise<CfMeta | undefined> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return readCfMetaFromVars(parsed.vars.entries);\n}\n\nexport async function writeCfMetaToFile(\n path: string,\n ref: CfAppRef,\n baseUrl?: string,\n): Promise<boolean> {\n const raw = await readFile(path, \"utf8\");\n const updates = buildCfMetaUpdates(ref, baseUrl);\n const { content, changed } = upsertVars(raw, updates);\n if (changed) {\n await writeFile(path, content, \"utf8\");\n }\n return changed;\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseBruEnvFile } from \"./bru-parser.js\";\nimport {\n ENVIRONMENTS_DIR,\n ORG_FOLDER_PREFIX,\n parsePrefixedName,\n REGION_FOLDER_PREFIX,\n SPACE_FOLDER_PREFIX,\n} from \"./paths.js\";\nimport type {\n AppFolder,\n BrunoCollection,\n BruEnvFile,\n OrgFolder,\n RegionFolder,\n SpaceFolder,\n} from \"./types.js\";\n\nasync function safeReaddir(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function listFiles(path: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(path, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function loadEnvFile(path: string, name: string): Promise<BruEnvFile> {\n const raw = await readFile(path, \"utf8\");\n const parsed = parseBruEnvFile(raw);\n return {\n path,\n name: name.replace(/\\.bru$/, \"\"),\n raw,\n vars: parsed.vars,\n secrets: parsed.secrets,\n };\n}\n\nasync function scanAppEnvironments(appPath: string): Promise<readonly BruEnvFile[]> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n const files = await listFiles(envDir);\n const bruFiles = files.filter((f) => f.endsWith(\".bru\"));\n const loaded: BruEnvFile[] = [];\n for (const file of bruFiles) {\n loaded.push(await loadEnvFile(join(envDir, file), file));\n }\n return loaded;\n}\n\nasync function scanApp(spacePath: string, name: string): Promise<AppFolder> {\n const appPath = join(spacePath, name);\n const environments = await scanAppEnvironments(appPath);\n return { path: appPath, name, environments };\n}\n\nasync function scanSpace(orgPath: string, dirName: string): Promise<SpaceFolder | undefined> {\n const name = parsePrefixedName(dirName, SPACE_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const spacePath = join(orgPath, dirName);\n const appDirs = await safeReaddir(spacePath);\n const apps: AppFolder[] = [];\n for (const appDir of appDirs) {\n apps.push(await scanApp(spacePath, appDir));\n }\n return { path: spacePath, name, apps };\n}\n\nasync function scanOrg(regionPath: string, dirName: string): Promise<OrgFolder | undefined> {\n const name = parsePrefixedName(dirName, ORG_FOLDER_PREFIX);\n if (name === undefined) {\n return undefined;\n }\n const orgPath = join(regionPath, dirName);\n const spaceDirs = await safeReaddir(orgPath);\n const spaces: SpaceFolder[] = [];\n for (const spaceDir of spaceDirs) {\n const space = await scanSpace(orgPath, spaceDir);\n if (space) {\n spaces.push(space);\n }\n }\n return { path: orgPath, name, spaces };\n}\n\nasync function scanRegion(root: string, dirName: string): Promise<RegionFolder | undefined> {\n const key = parsePrefixedName(dirName, REGION_FOLDER_PREFIX);\n if (key === undefined) {\n return undefined;\n }\n const regionPath = join(root, dirName);\n const orgDirs = await safeReaddir(regionPath);\n const orgs: OrgFolder[] = [];\n for (const orgDir of orgDirs) {\n const org = await scanOrg(regionPath, orgDir);\n if (org) {\n orgs.push(org);\n }\n }\n return { path: regionPath, key, orgs };\n}\n\nexport async function scanCollection(root: string): Promise<BrunoCollection> {\n const regionDirs = await safeReaddir(root);\n const regions: RegionFolder[] = [];\n for (const dir of regionDirs) {\n const region = await scanRegion(root, dir);\n if (region) {\n regions.push(region);\n }\n }\n return { root, regions };\n}\n\nexport interface ShorthandRef {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly environment?: string;\n readonly filePath?: string;\n}\n\nexport function parseShorthandPath(shorthand: string): ShorthandRef | undefined {\n const cleaned = shorthand.replace(/^[./]+/, \"\").replace(/\\\\/g, \"/\");\n const segs = cleaned.split(\"/\").filter((s) => s.length > 0);\n if (segs.length < 4) {\n return undefined;\n }\n const [region, org, space, app, ...rest] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n if (rest.length === 0) {\n return { region, org, space, app };\n }\n const filePath = rest.join(\"/\");\n const last = rest[rest.length - 1] ?? \"\";\n const environment = last.endsWith(\".bru\") ? last.replace(/\\.bru$/, \"\") : undefined;\n return environment\n ? { region, org, space, app, environment, filePath }\n : { region, org, space, app, filePath };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { brunoContextPath } from \"./paths.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport async function readContext(): Promise<BrunoContext | undefined> {\n try {\n const raw = await readFile(brunoContextPath(), \"utf8\");\n return JSON.parse(raw) as BrunoContext;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n}\n\nexport async function writeContext(ctx: Omit<BrunoContext, \"updatedAt\">): Promise<BrunoContext> {\n const updated: BrunoContext = { ...ctx, updatedAt: new Date().toISOString() };\n const path = brunoContextPath();\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(updated, null, 2)}\\n`, \"utf8\");\n return updated;\n}\n","import { mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\n\nimport type { OrgNode, RegionKey, RegionNode, SpaceNode } from \"@saptools/cf-sync\";\n\nimport type { CfInfoDeps } from \"./cf-info.js\";\nimport { defaultCfInfoDeps, listRegionsWithContent } from \"./cf-info.js\";\nimport { writeCfMetaToFile } from \"./cf-meta.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\nimport type { CfAppRef } from \"./types.js\";\n\nexport interface EnvironmentSelection {\n readonly common: readonly string[];\n readonly existing: readonly string[];\n}\n\nexport interface SetupAppPrompts {\n readonly selectRegion: (choices: readonly { value: RegionKey; name: string }[]) => Promise<RegionKey>;\n readonly selectOrg: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectSpace: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly selectApp: (choices: readonly { value: string; name: string }[]) => Promise<string>;\n readonly confirmCreate: (path: string) => Promise<boolean>;\n readonly selectEnvironments: (opts: EnvironmentSelection) => Promise<readonly string[]>;\n}\n\nexport interface SetupAppOptions {\n readonly root: string;\n readonly prompts: SetupAppPrompts;\n readonly deps?: CfInfoDeps;\n readonly log?: (msg: string) => void;\n}\n\nexport interface SetupAppResult {\n readonly ref: CfAppRef;\n readonly appPath: string;\n readonly environments: readonly string[];\n readonly created: boolean;\n}\n\nexport const COMMON_ENVIRONMENTS = [\"local\", \"dev\", \"staging\", \"prod\"] as const;\nconst BRUNO_COLLECTION_CONFIG_FILENAME = \"bruno.json\";\n\nexport const ENV_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;\n\nexport function assertValidEnvName(name: string): void {\n if (!ENV_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid environment name '${name}': only letters, digits, dot, underscore, and dash are allowed.`,\n );\n }\n}\n\nfunction emptyEnvContent(envName: string, ref: CfAppRef): string {\n const lines = [\n \"vars {\",\n ` __cf_region: ${ref.region}`,\n ` __cf_org: ${ref.org}`,\n ` __cf_space: ${ref.space}`,\n ` __cf_app: ${ref.app}`,\n ` environment: ${envName}`,\n \" baseUrl: \",\n \"}\",\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\nfunction normalizeCollectionName(root: string): string {\n const candidate = basename(root).replace(/^\\.+/, \"\").trim();\n return candidate.length > 0 ? candidate : \"bruno-collection\";\n}\n\nfunction defaultBrunoConfig(root: string): string {\n return `${JSON.stringify(\n {\n version: \"1\",\n name: normalizeCollectionName(root),\n type: \"collection\",\n ignore: [\"node_modules\", \".git\"],\n },\n null,\n 2,\n )}\\n`;\n}\n\nasync function ensureCollectionConfig(root: string): Promise<void> {\n const filePath = join(root, BRUNO_COLLECTION_CONFIG_FILENAME);\n try {\n await writeFile(filePath, defaultBrunoConfig(root), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n }\n}\n\nasync function ensureEnvFile(appPath: string, envName: string, ref: CfAppRef): Promise<string> {\n const envDir = join(appPath, ENVIRONMENTS_DIR);\n await mkdir(envDir, { recursive: true });\n const filePath = join(envDir, `${envName}.bru`);\n try {\n await writeFile(filePath, emptyEnvContent(envName, ref), { encoding: \"utf8\", flag: \"wx\" });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw err;\n }\n await writeCfMetaToFile(filePath, ref);\n }\n return filePath;\n}\n\nfunction pickRegion(regions: readonly { key: RegionKey; label: string; orgCount: number }[]): readonly {\n readonly value: RegionKey;\n readonly name: string;\n}[] {\n return regions.map((r) => ({ value: r.key, name: `${r.key} โ€” ${r.label} (${r.orgCount.toString()} org${r.orgCount === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickOrg(region: RegionNode): readonly { value: string; name: string }[] {\n return region.orgs.map((o) => ({ value: o.name, name: `${o.name} (${o.spaces.length.toString()} space${o.spaces.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickSpace(org: OrgNode): readonly { value: string; name: string }[] {\n return org.spaces.map((s) => ({ value: s.name, name: `${s.name} (${s.apps.length.toString()} app${s.apps.length === 1 ? \"\" : \"s\"})` }));\n}\n\nfunction pickApp(space: SpaceNode): readonly { value: string; name: string }[] {\n return space.apps.map((a) => ({ value: a.name, name: a.name }));\n}\n\nexport async function setupApp(options: SetupAppOptions): Promise<SetupAppResult> {\n const deps = options.deps ?? defaultCfInfoDeps;\n const log = options.log ?? ((): void => undefined);\n\n const regions = await listRegionsWithContent(deps);\n if (regions.length === 0) {\n throw new Error(\n \"No CF regions with orgs are cached. Run `cf-sync sync` first, or pass SAP_EMAIL/SAP_PASSWORD to refresh.\",\n );\n }\n\n const regionKey = await options.prompts.selectRegion(pickRegion(regions));\n const regionView = await deps.readRegionView(regionKey);\n if (!regionView) {\n throw new Error(`Region ${regionKey} is not cached. Run \\`cf-sync sync\\` or \\`cf-sync region ${regionKey}\\`.`);\n }\n const region = regionView.region;\n\n if (region.orgs.length === 0) {\n throw new Error(`Region ${regionKey} has no accessible orgs.`);\n }\n\n const orgName = await options.prompts.selectOrg(pickOrg(region));\n const org = region.orgs.find((o) => o.name === orgName);\n if (!org) {\n throw new Error(`Org ${orgName} not found in region ${regionKey}`);\n }\n if (org.spaces.length === 0) {\n throw new Error(`Org ${orgName} has no spaces.`);\n }\n\n const spaceName = await options.prompts.selectSpace(pickSpace(org));\n const space = org.spaces.find((s) => s.name === spaceName);\n if (!space) {\n throw new Error(`Space ${spaceName} not found in org ${orgName}`);\n }\n if (space.apps.length === 0) {\n throw new Error(`Space ${spaceName} has no apps.`);\n }\n\n const appName = await options.prompts.selectApp(pickApp(space));\n const ref: CfAppRef = { region: regionKey, org: orgName, space: spaceName, app: appName };\n\n const appPath = join(\n options.root,\n regionFolderName(regionKey),\n orgFolderName(orgName),\n spaceFolderName(spaceName),\n appName,\n );\n\n const confirmed = await options.prompts.confirmCreate(appPath);\n if (!confirmed) {\n return { ref, appPath, environments: [], created: false };\n }\n\n await mkdir(options.root, { recursive: true });\n await ensureCollectionConfig(options.root);\n await mkdir(appPath, { recursive: true });\n\n const existingEnvs = await listExistingEnvs(appPath);\n const common = [...COMMON_ENVIRONMENTS];\n const selected = await options.prompts.selectEnvironments({ common, existing: existingEnvs });\n const merged: string[] = [];\n for (const name of selected) {\n const trimmed = name.trim();\n if (trimmed.length === 0 || merged.includes(trimmed)) {\n continue;\n }\n assertValidEnvName(trimmed);\n merged.push(trimmed);\n }\n if (merged.length === 0) {\n throw new Error(\"At least one environment is required.\");\n }\n\n const created: string[] = [];\n for (const envName of merged) {\n const path = await ensureEnvFile(appPath, envName, ref);\n created.push(path);\n log(`โ€ข ${path}`);\n }\n\n return { ref, appPath, environments: created, created: true };\n}\n\nasync function listExistingEnvs(appPath: string): Promise<readonly string[]> {\n try {\n const entries = await readdir(join(appPath, ENVIRONMENTS_DIR), { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith(\".bru\"))\n .map((e) => e.name.replace(/\\.bru$/, \"\"));\n } catch {\n return [];\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\nimport type { AppRef } from \"@saptools/cf-xsuaa\";\nimport { getTokenCached as getTokenCachedApi } from \"@saptools/cf-xsuaa\";\n\nimport { readCfMetaFromFile } from \"./cf-meta.js\";\nimport type { ShorthandRef } from \"./folder-scan.js\";\nimport { parseShorthandPath, scanCollection } from \"./folder-scan.js\";\nimport {\n ENVIRONMENTS_DIR,\n orgFolderName,\n regionFolderName,\n spaceFolderName,\n} from \"./paths.js\";\n\nexport type GetTokenCachedFn = (ref: AppRef) => Promise<string>;\n\nexport interface RunSpawnResult {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport type SpawnBruFn = (\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n) => Promise<RunSpawnResult>;\n\nexport interface RunOptions {\n readonly root: string;\n readonly target: string;\n readonly environment?: string;\n readonly extraArgs?: readonly string[];\n readonly getTokenCached?: GetTokenCachedFn;\n readonly spawnBru?: SpawnBruFn;\n readonly log?: (msg: string) => void;\n}\n\nexport interface RunPlan {\n readonly filePath: string;\n readonly environment: string;\n readonly envFile: string;\n readonly meta: { readonly region: string; readonly org: string; readonly space: string; readonly app: string };\n readonly token: string;\n readonly bruArgs: readonly string[];\n readonly cwd: string;\n}\n\nexport interface RunResult extends RunPlan {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nconst require = createRequire(import.meta.url);\n\nexport interface BruRuntime {\n readonly command: string;\n readonly argsPrefix: readonly string[];\n}\n\nexport interface ResolveBruRuntimeDeps {\n readonly findOnPath?: (command: string, env: NodeJS.ProcessEnv) => Promise<string | undefined>;\n readonly readTextFile?: (path: string) => Promise<string>;\n readonly resolvePackageJsonPath?: () => string;\n}\n\nfunction pathEntries(env: NodeJS.ProcessEnv): readonly string[] {\n const value = env[\"PATH\"] ?? process.env[\"PATH\"] ?? \"\";\n return value.split(delimiter).filter((entry) => entry.length > 0);\n}\n\nfunction pathCandidates(command: string, env: NodeJS.ProcessEnv): readonly string[] {\n if (process.platform !== \"win32\" || command.includes(\".\")) {\n return [command];\n }\n const pathExt =\n env[\"PATHEXT\"]?.split(\";\").filter((entry) => entry.length > 0) ?? [\".COM\", \".EXE\", \".BAT\", \".CMD\"];\n return [command, ...pathExt.map((ext) => `${command}${ext}`)];\n}\n\nasync function findCommandOnPath(command: string, env: NodeJS.ProcessEnv): Promise<string | undefined> {\n const candidates = pathCandidates(command, env);\n for (const entry of pathEntries(env)) {\n for (const candidate of candidates) {\n const fullPath = join(entry, candidate);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction bruBinRelativePath(value: unknown): string | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const bin = value[\"bin\"];\n if (typeof bin === \"string\") {\n return bin;\n }\n if (!isRecord(bin)) {\n return undefined;\n }\n const bru = bin[\"bru\"];\n return typeof bru === \"string\" ? bru : undefined;\n}\n\nfunction defaultResolvePackageJsonPath(): string {\n return require.resolve(\"@usebruno/cli/package.json\");\n}\n\nasync function defaultReadTextFile(path: string): Promise<string> {\n return await readFile(path, \"utf8\");\n}\n\nasync function resolveBundledBruBinPath(\n deps: ResolveBruRuntimeDeps,\n): Promise<string | undefined> {\n try {\n const packageJsonPath = (deps.resolvePackageJsonPath ?? defaultResolvePackageJsonPath)();\n const raw = await (deps.readTextFile ?? defaultReadTextFile)(packageJsonPath);\n const binPath = bruBinRelativePath(JSON.parse(raw) as unknown);\n if (!binPath) {\n return undefined;\n }\n return resolve(dirname(packageJsonPath), binPath);\n } catch {\n return undefined;\n }\n}\n\nexport async function resolveBruRuntime(\n env: NodeJS.ProcessEnv = process.env,\n deps: ResolveBruRuntimeDeps = {},\n): Promise<BruRuntime> {\n const onPath = await (deps.findOnPath ?? findCommandOnPath)(\"bru\", env);\n if (onPath) {\n return { command: onPath, argsPrefix: [] };\n }\n const bundledBin = await resolveBundledBruBinPath(deps);\n if (bundledBin) {\n return { command: process.execPath, argsPrefix: [bundledBin] };\n }\n throw new Error(\n \"Unable to find Bruno CLI. Install @usebruno/cli or ensure `bru` is available on PATH.\",\n );\n}\n\nasync function defaultSpawnBru(\n args: readonly string[],\n env: NodeJS.ProcessEnv,\n cwd: string,\n): Promise<RunSpawnResult> {\n const runtime = await resolveBruRuntime(env);\n return await new Promise((resolvePromise, rejectPromise) => {\n const child = spawn(runtime.command, [...runtime.argsPrefix, ...args], { cwd, env, stdio: \"pipe\" });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf8\");\n process.stdout.write(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n process.stderr.write(chunk);\n });\n child.on(\"error\", rejectPromise);\n child.on(\"close\", (code) => {\n resolvePromise({ code: code ?? 0, stdout, stderr });\n });\n });\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveTarget(\n root: string,\n target: string,\n): Promise<{ readonly filePath: string; readonly shorthand: ShorthandRef | undefined }> {\n const direct = isAbsolute(target) ? target : resolve(process.cwd(), target);\n if (await exists(direct)) {\n return { filePath: direct, shorthand: undefined };\n }\n\n const shorthand = parseShorthandPath(target);\n if (!shorthand) {\n throw new Error(`Target not found: ${target}`);\n }\n\n const { region, org, space, app, filePath } = shorthand;\n const appDir = join(\n root,\n regionFolderName(region),\n orgFolderName(org),\n spaceFolderName(space),\n app,\n );\n\n if (!filePath) {\n return { filePath: appDir, shorthand };\n }\n\n const candidate = join(appDir, filePath);\n if (await exists(candidate)) {\n return { filePath: candidate, shorthand };\n }\n\n const withExt = candidate.endsWith(\".bru\") ? candidate : `${candidate}.bru`;\n if (await exists(withExt)) {\n return { filePath: withExt, shorthand };\n }\n\n throw new Error(`File not found: ${candidate}`);\n}\n\nasync function chooseEnvironmentFile(\n appDir: string,\n environment: string | undefined,\n): Promise<{ readonly envFile: string; readonly environment: string }> {\n if (environment) {\n const envFile = join(appDir, ENVIRONMENTS_DIR, `${environment}.bru`);\n if (!(await exists(envFile))) {\n throw new Error(`Environment file not found: ${envFile}`);\n }\n return { envFile, environment };\n }\n\n const collection = await scanCollection(resolve(appDir, \"..\", \"..\", \"..\", \"..\"));\n for (const region of collection.regions) {\n for (const org of region.orgs) {\n for (const space of org.spaces) {\n for (const app of space.apps) {\n if (app.path === appDir && app.environments.length > 0) {\n const first = app.environments[0];\n if (first) {\n return { envFile: first.path, environment: first.name };\n }\n }\n }\n }\n }\n }\n throw new Error(`No environment files found under ${appDir}/${ENVIRONMENTS_DIR}`);\n}\n\nfunction findAppDirFromFile(filePath: string, root: string): string {\n const rel = relative(root, filePath).split(sep);\n if (rel.length < 4) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n const [regionDir, orgDir, spaceDir, appDir] = rel;\n if (!regionDir || !orgDir || !spaceDir || !appDir) {\n throw new Error(`File is not inside a CF-structured bruno collection: ${filePath}`);\n }\n return join(root, regionDir, orgDir, spaceDir, appDir);\n}\n\nexport async function buildRunPlan(options: RunOptions): Promise<RunPlan> {\n const { filePath } = await resolveTarget(options.root, options.target);\n const stats = await stat(filePath);\n\n let appDir: string;\n let requestFile: string | undefined;\n\n if (stats.isDirectory()) {\n appDir = filePath;\n requestFile = undefined;\n } else {\n appDir = findAppDirFromFile(filePath, options.root);\n requestFile = filePath;\n }\n\n const { envFile, environment } = await chooseEnvironmentFile(appDir, options.environment);\n\n const meta = await readCfMetaFromFile(envFile);\n if (!meta) {\n throw new Error(\n `Missing __cf_region/__cf_org/__cf_space/__cf_app in ${envFile}. Run \\`saptools-bruno setup-app\\` first.`,\n );\n }\n\n const getToken = options.getTokenCached ?? getTokenCachedApi;\n const token = await getToken(meta);\n\n const bruArgs: string[] = [\"run\"];\n if (requestFile) {\n bruArgs.push(relative(appDir, requestFile) || \".\");\n }\n bruArgs.push(\"--env\", environment, \"--env-var\", `accessToken=${token}`);\n if (options.extraArgs) {\n bruArgs.push(...options.extraArgs);\n }\n\n return {\n filePath,\n environment,\n envFile,\n meta,\n token,\n bruArgs,\n cwd: appDir,\n };\n}\n\nexport async function runBruno(options: RunOptions): Promise<RunResult> {\n const plan = await buildRunPlan(options);\n const spawnFn = options.spawnBru ?? defaultSpawnBru;\n const env: NodeJS.ProcessEnv = { ...process.env, SAPTOOLS_ACCESS_TOKEN: plan.token };\n options.log?.(`โ–ถ bru ${plan.bruArgs.join(\" \")} (cwd=${plan.cwd})`);\n const result = await spawnFn(plan.bruArgs, env, plan.cwd);\n return { ...plan, ...result };\n}\n","import type { CfInfoDeps } from \"./cf-info.js\";\nimport { isValidRegionKey, resolveRef } from \"./cf-info.js\";\nimport { writeContext } from \"./context.js\";\nimport type { BrunoContext } from \"./types.js\";\n\nexport function parseContextShorthand(shorthand: string): {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n} | undefined {\n const segs = shorthand.split(\"/\").filter((s) => s.length > 0);\n if (segs.length !== 4) {\n return undefined;\n }\n const [region, org, space, app] = segs;\n if (!region || !org || !space || !app) {\n return undefined;\n }\n return { region, org, space, app };\n}\n\nexport interface UseOptions {\n readonly shorthand: string;\n readonly deps?: CfInfoDeps;\n readonly verify?: boolean;\n}\n\nexport async function useContext(options: UseOptions): Promise<BrunoContext> {\n const parsed = parseContextShorthand(options.shorthand);\n if (!parsed) {\n throw new Error(\n `Invalid context shorthand: ${options.shorthand}. Expected <region>/<org>/<space>/<app>.`,\n );\n }\n\n if (!isValidRegionKey(parsed.region)) {\n throw new Error(`Unknown region key: ${parsed.region}`);\n }\n\n if (options.verify !== false) {\n const resolved = await resolveRef({ ...parsed, region: parsed.region }, options.deps);\n if (!resolved) {\n throw new Error(\n `Could not verify ${options.shorthand} against the cached CF structure. Run \\`cf-sync sync\\` first.`,\n );\n }\n }\n\n return await writeContext(parsed);\n}\n"],"mappings":"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","names":["sep","readFile","join","readFile","join","readFile","writeFile","readFile","writeFile","mkdir","readdir","writeFile","join","join","writeFile","mkdir","readdir","readFile","dirname","join","require","join","readFile","dirname"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saptools/bruno",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Smart runner for Bruno โ€” CF-aware env metadata, automatic token injection, and shorthand paths",
5
5
  "type": "module",
6
6
  "publishConfig": {