From af52c7baf9bfba3e235fbb92ee72beebefa6c552 Mon Sep 17 00:00:00 2001 From: pbean Date: Fri, 29 May 2026 20:30:46 -0700 Subject: [PATCH] feat(bmad-module): distribute installed skills to the user's chosen IDEs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bmad-module skill staged community modules under _bmad// but never pushed their skills out to the coding assistants the user selected at `bmad install` time, so a freshly installed module was invisible to Claude Code / Cursor / Copilot / etc. until a full reinstall; remove left skills orphaned in the IDE dirs. install/update/remove now distribute (or prune) skills to every IDE listed in _bmad/_config/manifest.yaml and clean the redundant skill dirs from _bmad/, matching how official modules end up. Single engine, three callers — no fork: - New tools/installer/core/ide-sync.js (syncIdes) wraps the real IdeManager.setupBatch + platform-codes engine. The full installer (_setupIdes/_cleanupSkillDirs), the new `bmad ide-sync` command, and the skill all route through it, so new IDEs and engine changes propagate everywhere automatically. Local, dependency-free delivery — no npx/network at runtime: - build-ide-sync.mjs esbuild-bundles the engine into vendor/ide-sync.mjs (+ platform-codes.yaml), aliasing ../prompts and ../project-root to small shims so @clack and the installer graph are dropped. The bundle ships inside the skill tree (like yaml.mjs); the skill execs it locally. It's generated-from-source and gated by vendor:check, refreshed on every install. update/remove pass --prune with the module's canonicalIds so skills dropped between versions (or on uninstall) are removed from IDE dirs + command pointers. Graceful degradation: if the bundle is unreachable, the verb still succeeds and points the user at `bmad ide-sync`. Tests: new test/test-ide-sync.js drift-guard (engine == bundle, incl. prune), integration.test.sh IDE-distribution section (offline), bundle self-check in the build. All gates green (vendor:check, lint, format, test:install 349/349). Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 9 +- src/core-skills/bmad-module/README.md | 7 +- src/core-skills/bmad-module/SKILL.md | 11 +- .../bmad-module/scripts/install.mjs | 31 +- .../bmad-module/scripts/lib/ide-sync.mjs | 55 + .../bmad-module/scripts/lib/manifest-ops.mjs | 14 + .../bmad-module/scripts/lib/vendor/README.md | 10 + .../scripts/lib/vendor/build-ide-sync.mjs | 192 + .../scripts/lib/vendor/ide-sync.mjs | 10362 ++++++++++++++++ .../scripts/lib/vendor/platform-codes.yaml | 322 + .../scripts/lib/vendor/shims/project-root.cjs | 18 + .../scripts/lib/vendor/shims/prompts.cjs | 53 + .../bmad-module/scripts/remove.mjs | 14 + .../bmad-module/scripts/update.mjs | 14 + .../bmad-module/tests/integration.test.sh | 38 + test/test-ide-sync.js | 123 + tools/installer/commands/ide-sync.js | 34 + tools/installer/core/ide-sync.js | 215 + tools/installer/core/installer.js | 38 +- tools/installer/ide/platform-codes.js | 15 +- 20 files changed, 11532 insertions(+), 43 deletions(-) create mode 100644 src/core-skills/bmad-module/scripts/lib/ide-sync.mjs create mode 100644 src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs create mode 100644 src/core-skills/bmad-module/scripts/lib/vendor/ide-sync.mjs create mode 100644 src/core-skills/bmad-module/scripts/lib/vendor/platform-codes.yaml create mode 100644 src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs create mode 100644 src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs create mode 100644 test/test-ide-sync.js create mode 100644 tools/installer/commands/ide-sync.js create mode 100644 tools/installer/core/ide-sync.js diff --git a/package.json b/package.json index c7c7e6f59..3f9e89f70 100644 --- a/package.json +++ b/package.json @@ -40,17 +40,18 @@ "lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix", "lint:md": "markdownlint-cli2 \"**/*.md\"", "prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0", - "quality": "npm run vendor:check && npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar", + "quality": "npm run vendor:check && npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:ide-sync && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar", "rebundle": "node tools/installer/bundlers/bundle-web.js rebundle", - "test": "npm run vendor:check && npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run lint && npm run lint:md && npm run format:check", + "test": "npm run vendor:check && npm run test:refs && npm run test:install && npm run test:ide-sync && npm run test:urls && npm run test:channels && npm run lint && npm run lint:md && npm run format:check", "test:channels": "node test/test-installer-channels.js", + "test:ide-sync": "node test/test-ide-sync.js", "test:install": "node test/test-installation-components.js", "test:refs": "node test/test-file-refs-csv.js", "test:urls": "node test/test-parse-source-urls.js", "validate:refs": "node tools/validate-file-refs.js --strict", "validate:skills": "node tools/validate-skills.js --strict", - "vendor:build": "node src/core-skills/bmad-module/scripts/lib/vendor/build-vendor.mjs", - "vendor:check": "node src/core-skills/bmad-module/scripts/lib/vendor/build-vendor.mjs --check" + "vendor:build": "node src/core-skills/bmad-module/scripts/lib/vendor/build-vendor.mjs && node src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs", + "vendor:check": "node src/core-skills/bmad-module/scripts/lib/vendor/build-vendor.mjs --check && node src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs --check" }, "lint-staged": { "*.{js,cjs,mjs}": [ diff --git a/src/core-skills/bmad-module/README.md b/src/core-skills/bmad-module/README.md index 928bf3f5c..ecfaa30f7 100644 --- a/src/core-skills/bmad-module/README.md +++ b/src/core-skills/bmad-module/README.md @@ -5,7 +5,7 @@ The core BMAD skill for installing, updating, removing, and listing community BM ## How it fits - **Authors** publish a single repo with `.claude-plugin/plugin.json` that works in both Claude Code's plugin marketplace and BMAD-METHOD. -- **Users** install via this skill — no CLI required. Modules land in `_bmad//` alongside the official modules. +- **Users** install via this skill — no CLI required. Modules are staged under `_bmad//`, then their skills are distributed to the coding assistants the user chose at `bmad install` time (the `ides:` list in `_bmad/_config/manifest.yaml`), exactly like official modules. - **BMAD-METHOD** treats community-installed modules as a new `source: 'community'` row in `manifest.yaml`; re-running `bmad install` preserves them (with the paired `manifest-generator.js` patch). ## Verbs @@ -23,8 +23,9 @@ bmad-module list [--json] - **Source of truth** for what was installed is `_bmad/_config/files-manifest.csv` (per-file hashes) and `_bmad/_config/skill-manifest.csv` (one row per shipped skill). `manifest.yaml` carries the source/version/sha tuple. - **`update`** refuses to overwrite locally-modified files (hash mismatch against the recorded hash). Move overrides into `_bmad/custom//` and retry. -- **`remove`** without `--purge` preserves `_bmad/custom//` so a re-install picks the customizations back up. `--purge` deletes them. -- **Hooks / MCP / LSP / Claude subagents** declared in the module manifest are _copied_ but NOT auto-activated by this skill. Use Claude Code's plugin manager to wire them up. +- **`remove`** without `--purge` preserves `_bmad/custom//` so a re-install picks the customizations back up. `--purge` deletes them. Remove also prunes the module's skills from every configured IDE. +- **IDE distribution** runs after every install/update/remove via a self-contained bundle of BMAD's real IDE engine, shipped at `lib/vendor/ide-sync.mjs` (built from `tools/installer/ide/*` by `lib/vendor/build-ide-sync.mjs`, gated by `vendor:check`). The skill execs it locally — no npx, no network. The same engine also backs the `bmad ide-sync` CLI command and the full installer's IDE setup, so all three stay in lockstep. If the bundle is unreachable on an older install, the skill says so and points the user at `bmad ide-sync`. +- **Hooks / MCP / LSP / Claude subagents** declared in the module manifest are _copied_ but NOT auto-activated by this skill (they are Claude Code plugin surfaces, not skills). Use Claude Code's plugin manager to wire them up. ## Implementation diff --git a/src/core-skills/bmad-module/SKILL.md b/src/core-skills/bmad-module/SKILL.md index 083866fc4..97bfee5cc 100644 --- a/src/core-skills/bmad-module/SKILL.md +++ b/src/core-skills/bmad-module/SKILL.md @@ -5,11 +5,11 @@ description: Install, update, remove, or list community BMAD modules. Use when t # bmad-module -Manage community BMAD modules — installable packages of skills, agents, and supporting assets that ship as standalone GitHub repos. Modules land in `_bmad//` alongside official modules and are tracked in the existing manifests. The same artifact is also loadable as a Claude Code plugin via its `.claude-plugin/plugin.json` manifest. +Manage community BMAD modules — installable packages of skills, agents, and supporting assets that ship as standalone GitHub repos. Modules are staged under `_bmad//` and tracked in the existing manifests. On `install`, `update`, and `remove`, the script then distributes (or prunes) the module's skills to **every coding assistant the user selected when they ran `bmad install`** — read from the `ides:` list in `_bmad/_config/manifest.yaml` — so a community module lands in Claude Code, Cursor, Copilot, etc. exactly like an official module. As with official modules, the canonical end state is skills living in the IDE directories (e.g. `.claude/skills//`), not in `_bmad/`. The same artifact is also loadable as a Claude Code plugin via its `.claude-plugin/plugin.json` manifest. ## CRITICAL RULES -- NEVER write directly to files under `_bmad/`. All filesystem changes go through the Node script at `scripts/bmad-module.mjs` — it handles staging, atomic swaps, manifest updates, and rollback on failure. +- NEVER write directly to files under `_bmad/` or into IDE directories (`.claude/skills/`, `.agents/skills/`, etc.). All filesystem changes go through the Node script at `scripts/bmad-module.mjs` — it handles staging, atomic swaps, manifest updates, IDE distribution, and rollback on failure. - HALT and report cleanly if `_bmad/` is not present in the current working directory (exit code 10 from the script). - DO NOT execute hooks, MCP server commands, or any code shipped inside the module during install. The install copies files; activation is a separate step the user opts into via Claude Code's plugin manager. - If the script exits non-zero, report the exit code and stderr verbatim and stop. Do NOT retry, do NOT try a different verb. The one exception is exit code 5 (the skill's own bundled runtime files are missing/corrupt): that's a fixable setup/packaging problem, not a module rejection — relay the script's "reinstall the skill" guidance instead of reporting a failed install. @@ -63,7 +63,12 @@ Stream stdout and stderr verbatim. Do NOT silence or rewrite them — the script ### Step 5 — Report -On exit 0: paraphrase the script's final line(s) and note any next-step hint (e.g. "next: run the `bmad-devlog-setup` skill to finish setup"). +On exit 0: paraphrase the script's final line(s) and note any next-step hint (e.g. "next: run the `bmad-devlog-setup` skill to finish setup"). The script also prints `[ide-sync]` lines naming each coding assistant the skills were synced to — relay them so the user knows where the module landed. + +Note two non-fatal cases the script reports on exit 0: + +- If the script prints `[bmad-module] note: no coding assistants are configured…`, the module is staged under `_bmad/` but no IDEs were selected at `bmad install` time — tell the user to run `bmad install` to choose their assistants. +- If it prints `[bmad-module] warning:` about IDE distribution, the module installed fine but skills may not have reached every assistant — relay the script's suggestion to run `bmad ide-sync`. Do NOT treat this as a failed install. On non-zero exit: print the exit code, the stderr message, and stop. Do not suggest workarounds beyond what the script's message itself suggests (e.g. "use `update` instead", "move changes into `_bmad/custom//`"). diff --git a/src/core-skills/bmad-module/scripts/install.mjs b/src/core-skills/bmad-module/scripts/install.mjs index 750c2822e..4d7ff7b32 100644 --- a/src/core-skills/bmad-module/scripts/install.mjs +++ b/src/core-skills/bmad-module/scripts/install.mjs @@ -6,6 +6,7 @@ import { readAndValidateManifest } from './lib/plugin-json.mjs'; import { readUserIgnores, buildIgnoreMatcher, buildCopyPlan, rewriteManifestPaths, validateDeclaredPaths } from './lib/install-plan.mjs'; import { stageCopyPlan, atomicSwapDir } from './lib/fs-safe.mjs'; import { readManifestYaml, addModuleToManifest, appendSkillManifestRows, appendFilesManifestRows } from './lib/manifest-ops.mjs'; +import { distributeToIdes } from './lib/ide-sync.mjs'; // Run the install verb. `opts` shape: // { source, ref, sha, channel, dryRun, projectDir } @@ -102,22 +103,36 @@ export async function runInstall(opts) { await appendSkillManifestRows(bmadDir, code, skillDestDirs); await appendFilesManifestRows(bmadDir, code, destPaths); - // §8. Warn about Claude-only surfaces. + process.stdout.write( + `[bmad-module] installed ${code} (${manifest.name} ${manifest.version})${materialized.sha ? ` @ ${materialized.sha.slice(0, 7)}` : ''}\n`, + ); + process.stdout.write(`[bmad-module] copied ${destPaths.length} file(s) to ${path.relative(projectDir, targetDir)}\n`); + + // §8. Distribute the module's skills to the coding assistants the user chose + // at `bmad install` time (read from _bmad/_config/manifest.yaml). This is the + // same distribution the full installer performs; without it the skills would + // sit in _bmad/ and never reach Claude Code / Cursor / Copilot / etc. + const ideResult = await distributeToIdes({ projectDir, bmadDir }); + if (ideResult.skipped) { + process.stdout.write( + `[bmad-module] note: no coding assistants are configured in _bmad/_config/manifest.yaml — ` + + `skills are in _bmad/${code}/ only. Run \`bmad install\` to choose your IDEs.\n`, + ); + } else if (!ideResult.ok) { + process.stderr.write(`[bmad-module] warning: ${ideResult.hint}\n`); + } + + // §9. Warn about Claude-plugin-only surfaces (not distributed as skills). const claudeOnly = []; if (manifest.hooks) claudeOnly.push('hooks'); if (manifest.mcpServers) claudeOnly.push('mcpServers'); if (manifest.lspServers) claudeOnly.push('lspServers'); if (Array.isArray(manifest.agents) && manifest.agents.length) claudeOnly.push('agents'); if (Array.isArray(manifest.commands) && manifest.commands.length) claudeOnly.push('commands'); - - process.stdout.write( - `[bmad-module] installed ${code} (${manifest.name} ${manifest.version})${materialized.sha ? ` @ ${materialized.sha.slice(0, 7)}` : ''}\n`, - ); - process.stdout.write(`[bmad-module] copied ${destPaths.length} file(s) to ${path.relative(projectDir, targetDir)}\n`); if (claudeOnly.length) { process.stdout.write( - `[bmad-module] note: ${claudeOnly.join(', ')} were copied but NOT auto-activated. ` + - `Use Claude Code's plugin manager to wire them up.\n`, + `[bmad-module] note: ${claudeOnly.join(', ')} are Claude Code plugin surfaces and were copied but ` + + `NOT auto-activated. Use Claude Code's plugin manager to wire them up.\n`, ); } if (manifest.bmad?.install?.postInstallSkill) { diff --git a/src/core-skills/bmad-module/scripts/lib/ide-sync.mjs b/src/core-skills/bmad-module/scripts/lib/ide-sync.mjs new file mode 100644 index 000000000..b7832b613 --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/ide-sync.mjs @@ -0,0 +1,55 @@ +import { spawn } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { readManifestYaml } from './manifest-ops.mjs'; + +// Distribute the project's installed skills to the coding assistants (IDEs) the +// user chose at `bmad install` time, by running the self-contained engine bundle +// shipped beside this file (vendor/ide-sync.mjs). Pure node: + a local bundle — +// no npx, no network, no node_modules. The bundle reads the chosen IDEs from +// _bmad/_config/manifest.yaml and the skills from _config/skill-manifest.csv. +// +// `prune` is the list of canonicalIds to remove from the IDE directories (the +// skills of a module being updated or removed); pass [] for a plain install. +// +// Returns one of: +// { skipped: true } — no IDEs configured; nothing to do +// { ok: true } — bundle ran and distributed successfully +// { ok: false, hint } — bundle missing or exited non-zero; caller +// reports the hint but does NOT fail the verb +// (the _bmad/ write already succeeded). +export async function distributeToIdes({ projectDir, bmadDir, prune = [] }) { + const manifest = await readManifestYaml(bmadDir); + const ides = Array.isArray(manifest?.ides) ? manifest.ides.filter((i) => i && typeof i === 'string') : []; + if (ides.length === 0) { + return { skipped: true }; + } + + const bundlePath = fileURLToPath(new URL('vendor/ide-sync.mjs', import.meta.url)); + if (!existsSync(bundlePath)) { + return { + ok: false, + hint: + 'IDE distribution bundle is missing (older install). Run `bmad install` to refresh BMAD tooling, ' + + 'or `bmad ide-sync` to push skills to your coding assistants.', + }; + } + + const args = [bundlePath, '-d', projectDir]; + const pruneIds = (prune || []).filter(Boolean); + if (pruneIds.length) args.push('--prune', pruneIds.join(',')); + + const code = await new Promise((resolve) => { + const child = spawn(process.execPath, args, { stdio: 'inherit' }); + child.on('error', () => resolve(-1)); + child.on('close', (c) => resolve(c ?? -1)); + }); + + if (code === 0) return { ok: true }; + return { + ok: false, + hint: + `IDE distribution exited with code ${code}. Your module is installed under _bmad/, but skills may ` + + 'not be in every coding assistant yet — run `bmad ide-sync` to retry.', + }; +} diff --git a/src/core-skills/bmad-module/scripts/lib/manifest-ops.mjs b/src/core-skills/bmad-module/scripts/lib/manifest-ops.mjs index ae55b3fb6..e774aa748 100644 --- a/src/core-skills/bmad-module/scripts/lib/manifest-ops.mjs +++ b/src/core-skills/bmad-module/scripts/lib/manifest-ops.mjs @@ -222,6 +222,20 @@ export async function appendSkillManifestRows(bmadDir, code, skillDirs) { await fs.writeFile(csvPath, rowsToCsv(SKILL_HEADER, rows), 'utf8'); } +// Return the canonicalIds of a module's skills currently recorded in +// skill-manifest.csv. Used by update/remove to tell ide-sync which skill +// directories to prune from the IDE targets. +export async function readSkillCanonicalIdsForModule(bmadDir, code) { + const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); + const rows = await readCsvRows(csvPath); + if (!rows || rows.length < 2) return []; + return rows + .slice(1) + .filter((r) => r[3] === code) + .map((r) => r[0]) + .filter(Boolean); +} + export async function removeSkillManifestRows(bmadDir, code) { const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); const existingRaw = await readCsvRows(csvPath); diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/README.md b/src/core-skills/bmad-module/scripts/lib/vendor/README.md index 534c25e79..48a2b2b61 100644 --- a/src/core-skills/bmad-module/scripts/lib/vendor/README.md +++ b/src/core-skills/bmad-module/scripts/lib/vendor/README.md @@ -19,6 +19,7 @@ Files here are imported by **relative path** (`./vendor/yaml.mjs`), which resolv | Need | Strategy | Where | |---|---|---| | `yaml` (parse/stringify `_bmad/_config/manifest.yaml`) | **vendored, real library** | `vendor/yaml.mjs` | +| BMAD's IDE-distribution engine (push skills to `.claude/skills/` etc.) | **bundled, real engine** | `vendor/ide-sync.mjs` (+ `vendor/platform-codes.yaml`) | | `semver` (`valid` + `validRange` on `plugin.json`) | **dropped** — hand-rolled, `node:` only | `../semver-lite.mjs` | `manifest.yaml` is **co-owned** with BMAD core, which reads/writes it with the same `yaml` package and the same `{indent:2, lineWidth:0}` options (`tools/installer/core/manifest.js`). Hand-rolling a YAML emitter risks diverging from that on the user's live install state, so we ship the **real** library and verify byte-identical output in `build-vendor.mjs`. `semver` is only input-validation of an author's manifest, so it is safe to hand-roll. @@ -38,6 +39,15 @@ npm run vendor:build # regenerate this yaml.mjs npm run vendor:check # verify it's in sync (what CI runs) ``` +## `ide-sync.mjs` (+ `platform-codes.yaml`) + +- **GENERATED — do not edit by hand.** An esbuild bundle of BMAD's real IDE-distribution engine (`tools/installer/ide/*` reached via `tools/installer/core/ide-sync.js`), tree-shaken to a single dependency-free ESM file. Built by `build-ide-sync.mjs`; `../prompts` and `../project-root` are aliased to the small shims in `shims/` so the interactive `@clack/prompts` and installer-only graphs are dropped. +- After `bmad-module` stages a module under `_bmad/`, it execs this bundle to copy the module's skills into the IDE directories the user chose (read from `_bmad/_config/manifest.yaml`). Reasons it's bundled rather than imported are the same as `yaml.mjs`: the skill ships into projects without `node_modules`, and shelling out to `npx bmad-method` would reintroduce a network/npm dependency. +- `platform-codes.yaml` is copied verbatim beside the bundle (the engine reads it at runtime via `$BMAD_IDE_PLATFORM_CODES`, set by the bundle entry). +- Same engine backs `bmad ide-sync` and the full installer's IDE setup, so the three stay in lockstep; `vendor:check` byte-verifies the bundle against source and `test/test-ide-sync.js` checks engine/bundle behavior parity. + +Regenerated by the same commands as above (`vendor:build` / `vendor:check` run both bundles). + The build is **deterministic** for a given `yaml` + `esbuild` version (both pinned in the lockfile) and self-checks a parse→stringify round-trip. **You don't have to remember to do this.** `vendor:check` is wired into `npm test` (husky pre-commit) and `npm run quality` (the `validate` job in `.github/workflows/quality.yaml`). If the committed bundle drifts from the installed `yaml`/`esbuild` version, those gates fail with a message telling you to run `npm run vendor:build` — so a bump can't land with a stale bundle, and manifest writes stay byte-identical between BMAD core and this skill. diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs b/src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs new file mode 100644 index 000000000..f3b2c2e80 --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/vendor/build-ide-sync.mjs @@ -0,0 +1,192 @@ +#!/usr/bin/env node +// build-ide-sync — regenerates (and, with --check, verifies) the self-contained +// `ide-sync.mjs` bundle this skill ships, plus its sidecar `platform-codes.yaml`. +// +// Why this exists: after the bmad-module skill installs/updates/removes a +// community module under `_bmad/`, it must distribute that module's skills to +// exactly the coding assistants the user chose at `bmad install` time. The real +// distribution engine lives in `tools/installer/ide/*` (IdeManager / +// ConfigDrivenIdeSetup / platform-codes.yaml), but that code — and its +// dependencies (csv-parse, yaml, @clack/prompts) — is NOT present in a user's +// project (the installer ships the skill without node_modules). So we bundle the +// REAL engine into one dependency-free ESM file with esbuild (the same toolchain +// and vendoring philosophy as yaml.mjs), aliasing `../prompts` and +// `../project-root` to tiny shims so the interactive/heavy bits are dropped. +// +// The skill execs `vendor/ide-sync.mjs` from inside the user's project — no npx, +// no network, no node_modules. Because it is built from `tools/installer/ide/*` +// (not hand-forked) and verified by `vendor:check`, it can never silently drift +// from the engine the interactive installer uses. +// +// Usage (via root package.json): +// npm run vendor:build # regenerate ide-sync.mjs + platform-codes.yaml +// npm run vendor:check # fail if the committed bundle is stale (CI gate) + +import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { createRequire } from 'node:module'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const require = createRequire(import.meta.url); +const vendorDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(vendorDir, '../../../../../..'); // -> repo root +const installerDir = path.join(repoRoot, 'tools', 'installer'); +const ideDir = path.join(installerDir, 'ide'); + +const outfile = path.join(vendorDir, 'ide-sync.mjs'); +const sidecarOut = path.join(vendorDir, 'platform-codes.yaml'); +const sidecarSrc = path.join(ideDir, 'platform-codes.yaml'); + +const promptsShim = path.join(vendorDir, 'shims', 'prompts.cjs'); +const projectRootShim = path.join(vendorDir, 'shims', 'project-root.cjs'); + +const checkMode = process.argv.includes('--check'); + +const esbuild = await import('esbuild'); +const esbuildVersion = require('esbuild/package.json').version; +const yamlVersion = require('yaml/package.json').version; +// csv-parse restricts ./package.json via its "exports" map, so read it directly. +const csvVersion = JSON.parse(await fs.readFile(path.join(repoRoot, 'node_modules', 'csv-parse', 'package.json'), 'utf8')).version; + +// Redirect the engine's interactive/installer-only deps to lightweight shims so +// @clack/prompts and the custom-module-manager graph never enter the bundle. +const aliasPlugin = { + name: 'ide-sync-aliases', + setup(build) { + build.onResolve({ filter: /^\.\.\/prompts$/ }, () => ({ path: promptsShim })); + build.onResolve({ filter: /^\.\.\/project-root$/ }, () => ({ path: projectRootShim })); + }, +}; + +// NOTE: no builder-specific data (node version, timestamp) in the banner — the +// output must be reproducible so --check can byte-compare. +const banner = `// ============================================================================ +// GENERATED — DO NOT EDIT BY HAND. Run \`npm run vendor:build\` to regenerate. +// Self-contained bundle of BMAD's IDE-distribution engine +// (tools/installer/ide/* via tools/installer/core/ide-sync.js). +// +// bundler : esbuild ${esbuildVersion} +// yaml : ${yamlVersion} +// csv-parse : ${csvVersion} +// +// Shipped because the bmad-module skill is copied into projects without +// node_modules; see build-ide-sync.mjs and vendor/README.md for the rationale. +// Reads platform-codes.yaml from beside this file (or $BMAD_IDE_PLATFORM_CODES). +// ============================================================================ +// Provide a real \`require\` so esbuild's CJS interop can load node: builtins +// (node:path/fs/os/crypto) from this ESM bundle. All third-party deps are +// inlined, so only builtins ever reach this require. +import { createRequire as __createRequire } from 'node:module'; +const require = __createRequire(import.meta.url); +`; + +// The entry sets the platform-codes path to the sidecar beside the bundle, then +// runs the CLI. Imports are hoisted/initialised first; the engine reads the env +// var lazily (loadPlatformCodes), by which point the body below has set it. +const entryContents = ` +import { runIdeSyncCli } from './core/ide-sync.js'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const __dir = dirname(fileURLToPath(import.meta.url)); +if (!process.env.BMAD_IDE_PLATFORM_CODES) { + process.env.BMAD_IDE_PLATFORM_CODES = join(__dir, 'platform-codes.yaml'); +} + +runIdeSyncCli(process.argv.slice(2)) + .then((code) => process.exit(code)) + .catch((err) => { + process.stderr.write('[ide-sync] ' + ((err && err.stack) || err) + '\\n'); + process.exit(1); + }); +`; + +const result = await esbuild.build({ + stdin: { + contents: entryContents, + resolveDir: installerDir, // so './core/ide-sync.js' resolves + sourcefile: 'ide-sync-entry.mjs', + loader: 'js', + }, + bundle: true, + format: 'esm', + platform: 'node', + target: 'node20', + minify: false, + charset: 'utf8', + legalComments: 'inline', + banner: { js: banner }, + plugins: [aliasPlugin], + write: false, +}); +const built = result.outputFiles[0].text; +const sidecar = await fs.readFile(sidecarSrc, 'utf8'); + +// Self-check: the freshly built bundle must distribute a fixture skill to a +// selected IDE with no node_modules on its resolution path (the runtime +// condition). Build a throwaway project, run the bundle, assert the output. +await selfCheck(built, sidecar); + +if (checkMode) { + const currentBundle = await fs.readFile(outfile, 'utf8').catch(() => null); + const currentSidecar = await fs.readFile(sidecarOut, 'utf8').catch(() => null); + if (currentBundle === built && currentSidecar === sidecar) { + process.stdout.write(`vendor:check OK — ide-sync.mjs matches engine (esbuild ${esbuildVersion})\n`); + process.exit(0); + } + process.stderr.write( + `vendor:check FAILED — vendor/ide-sync.mjs or platform-codes.yaml is stale or hand-edited.\n` + + ` The committed bundle no longer matches tools/installer/ide/*.\n` + + ` Fix: run \`npm run vendor:build\` and commit the regenerated files.\n`, + ); + process.exit(1); +} + +await fs.writeFile(outfile, built, 'utf8'); +await fs.writeFile(sidecarOut, sidecar, 'utf8'); +process.stdout.write(`built ide-sync.mjs + platform-codes.yaml (self-check OK, esbuild ${esbuildVersion})\n`); + +// --------------------------------------------------------------------------- + +async function selfCheck(bundleText, sidecarText) { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-ide-sync-check-')); + try { + // Lay down the bundle + sidecar together. + await fs.writeFile(path.join(dir, 'ide-sync.mjs'), bundleText, 'utf8'); + await fs.writeFile(path.join(dir, 'platform-codes.yaml'), sidecarText, 'utf8'); + + // Minimal project: one community skill recorded in the manifests. + const proj = path.join(dir, 'proj'); + const skillDir = path.join(proj, '_bmad', 'demo', 'skills', 'bmad-demo-skill'); + await fs.mkdir(skillDir, { recursive: true }); + await fs.mkdir(path.join(proj, '_bmad', '_config'), { recursive: true }); + await fs.writeFile(path.join(skillDir, 'SKILL.md'), '---\nname: bmad-demo-skill\ndescription: demo\n---\nbody\n', 'utf8'); + await fs.writeFile( + path.join(proj, '_bmad', '_config', 'manifest.yaml'), + 'installation:\n version: "0.0.0"\nmodules:\n - name: demo\n source: community\nides:\n - claude-code\n', + 'utf8', + ); + await fs.writeFile( + path.join(proj, '_bmad', '_config', 'skill-manifest.csv'), + 'canonicalId,name,description,module,path\n"bmad-demo-skill","bmad-demo-skill","demo","demo","_bmad/demo/skills/bmad-demo-skill/SKILL.md"\n', + 'utf8', + ); + + const { spawn } = await import('node:child_process'); + const code = await new Promise((resolve) => { + const child = spawn(process.execPath, [path.join(dir, 'ide-sync.mjs'), '-d', proj], { + stdio: process.env.BMAD_IDE_SYNC_DEBUG ? 'inherit' : 'ignore', + }); + child.on('close', resolve); + }); + if (code !== 0) throw new Error(`ide-sync self-check: bundle exited ${code}`); + + const distributed = path.join(proj, '.claude', 'skills', 'bmad-demo-skill', 'SKILL.md'); + await fs.access(distributed).catch(() => { + throw new Error('ide-sync self-check FAILED: skill was not distributed to .claude/skills'); + }); + } finally { + await fs.rm(dir, { recursive: true, force: true }); + } +} diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/ide-sync.mjs b/src/core-skills/bmad-module/scripts/lib/vendor/ide-sync.mjs new file mode 100644 index 000000000..f81fd11a8 --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/vendor/ide-sync.mjs @@ -0,0 +1,10362 @@ +// ============================================================================ +// GENERATED — DO NOT EDIT BY HAND. Run `npm run vendor:build` to regenerate. +// Self-contained bundle of BMAD's IDE-distribution engine +// (tools/installer/ide/* via tools/installer/core/ide-sync.js). +// +// bundler : esbuild 0.25.12 +// yaml : 2.8.4 +// csv-parse : 6.1.0 +// +// Shipped because the bmad-module skill is copied into projects without +// node_modules; see build-ide-sync.mjs and vendor/README.md for the rationale. +// Reads platform-codes.yaml from beside this file (or $BMAD_IDE_PLATFORM_CODES). +// ============================================================================ +// Provide a real `require` so esbuild's CJS interop can load node: builtins +// (node:path/fs/os/crypto) from this ESM bundle. All third-party deps are +// inlined, so only builtins ever reach this require. +import { createRequire as __createRequire } from 'node:module'; +const require = __createRequire(import.meta.url); + +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// tools/installer/fs-native.js +var require_fs_native = __commonJS({ + "tools/installer/fs-native.js"(exports, module) { + var fsp = __require("node:fs/promises"); + var fs = __require("node:fs"); + var path = __require("node:path"); + async function pathExists(p) { + try { + await fsp.access(p); + return true; + } catch { + return false; + } + } + async function ensureDir(dir) { + await fsp.mkdir(dir, { recursive: true }); + } + async function remove(p) { + await fsp.rm(p, { recursive: true, force: true }); + } + async function copy(src, dest, options = {}) { + const filterFn = options.filter; + const overwrite = options.overwrite !== false; + const srcStat = await fsp.stat(src); + if (srcStat.isFile()) { + if (filterFn && !await filterFn(src, dest)) return; + await fsp.mkdir(path.dirname(dest), { recursive: true }); + if (!overwrite) { + try { + await fsp.access(dest); + if (options.errorOnExist) throw new Error(`${dest} already exists`); + return; + } catch (error) { + if (error.message.includes("already exists")) throw error; + } + } + await fsp.copyFile(src, dest); + return; + } + if (srcStat.isDirectory()) { + if (filterFn && !await filterFn(src, dest)) return; + await fsp.mkdir(dest, { recursive: true }); + const entries = await fsp.readdir(src, { withFileTypes: true }); + for (const entry of entries) { + await copy(path.join(src, entry.name), path.join(dest, entry.name), options); + } + } + } + async function move(src, dest) { + try { + await fsp.rename(src, dest); + } catch (error) { + if (error.code === "EXDEV") { + await copy(src, dest); + await fsp.rm(src, { recursive: true, force: true }); + } else { + throw error; + } + } + } + function readJsonSync(p) { + return JSON.parse(fs.readFileSync(p, "utf8")); + } + async function writeJson(p, data, options = {}) { + const spaces = options.spaces ?? 2; + await fsp.writeFile(p, JSON.stringify(data, null, spaces) + "\n", "utf8"); + } + module.exports = { + // Native async (node:fs/promises) + readFile: fsp.readFile, + writeFile: fsp.writeFile, + stat: fsp.stat, + readdir: fsp.readdir, + access: fsp.access, + realpath: fsp.realpath, + rename: fsp.rename, + rmdir: fsp.rmdir, + unlink: fsp.unlink, + chmod: fsp.chmod, + mkdir: fsp.mkdir, + mkdtemp: fsp.mkdtemp, + copyFile: fsp.copyFile, + rm: fsp.rm, + // fs-extra compatible helpers (native implementations) + pathExists, + ensureDir, + remove, + copy, + move, + readJsonSync, + writeJson, + // Sync methods from core node:fs + existsSync: fs.existsSync.bind(fs), + readFileSync: fs.readFileSync.bind(fs), + writeFileSync: fs.writeFileSync.bind(fs), + statSync: fs.statSync.bind(fs), + accessSync: fs.accessSync.bind(fs), + readdirSync: fs.readdirSync.bind(fs), + createReadStream: fs.createReadStream.bind(fs), + pathExistsSync: fs.existsSync.bind(fs), + // Constants + constants: fs.constants + }; + } +}); + +// tools/installer/ide/shared/path-utils.js +var require_path_utils = __commonJS({ + "tools/installer/ide/shared/path-utils.js"(exports, module) { + var AGENT_SEGMENT = "agents"; + var BMAD_FOLDER_NAME = "_bmad"; + function toDashName(module2, type, name) { + const isAgent = type === AGENT_SEGMENT; + if (module2 === "core") { + return isAgent ? `bmad-agent-${name}.md` : `bmad-${name}.md`; + } + if (module2 === "standalone") { + return isAgent ? `bmad-agent-standalone-${name}.md` : `bmad-standalone-${name}.md`; + } + const dashName = name.replace(/\//g, "-"); + return isAgent ? `bmad-agent-${module2}-${dashName}.md` : `bmad-${module2}-${dashName}.md`; + } + function toDashPath(relativePath) { + if (!relativePath || typeof relativePath !== "string") { + return "bmad-unknown.md"; + } + const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, ""); + const parts = withoutExt.split(/[/\\]/); + const module2 = parts[0]; + const type = parts[1]; + let name; + if (type === "agents" && parts.length > 3) { + name = parts[2]; + } else { + name = parts.slice(2).join("-"); + } + return toDashName(module2, type, name); + } + function customAgentDashName(agentName) { + return `bmad-custom-agent-${agentName}.md`; + } + function isDashFormat(filename) { + return filename.startsWith("bmad-") && filename.includes("-"); + } + function parseDashName(filename) { + const withoutExt = filename.replace(".md", ""); + const parts = withoutExt.split("-"); + if (parts.length < 2 || parts[0] !== "bmad") { + return null; + } + const isAgent = parts[1] === "agent"; + if (isAgent) { + if (parts.length >= 4 && parts[2] === "standalone") { + return { + prefix: parts[0], + module: "standalone", + type: "agents", + name: parts.slice(3).join("-") + }; + } + if (parts.length === 3) { + return { + prefix: parts[0], + module: "core", + type: "agents", + name: parts[2] + }; + } else { + return { + prefix: parts[0], + module: parts[2], + type: "agents", + name: parts.slice(3).join("-") + }; + } + } + if (parts.length === 2) { + return { + prefix: parts[0], + module: "core", + type: "workflows", + // Default to workflows for non-agent core items + name: parts[1] + }; + } + if (parts[1] === "standalone") { + return { + prefix: parts[0], + module: "standalone", + type: "workflows", + // Default to workflows for non-agent standalone items + name: parts.slice(2).join("-") + }; + } + return { + prefix: parts[0], + module: parts[1], + type: "workflows", + // Default to workflows for non-agent module items + name: parts.slice(2).join("-") + }; + } + function resolveSkillName(artifact) { + if (artifact.canonicalId) { + return `${artifact.canonicalId}.md`; + } + return toDashPath(artifact.relativePath); + } + module.exports = { + toDashName, + toDashPath, + resolveSkillName, + customAgentDashName, + isDashFormat, + parseDashName, + AGENT_SEGMENT, + BMAD_FOLDER_NAME + }; + } +}); + +// src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs +var require_prompts = __commonJS({ + "src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs"(exports, module) { + "use strict"; + var out = (m) => process.stdout.write(`${m} +`); + var err = (m) => process.stderr.write(`${m} +`); + var log = { + info: async (m) => out(m), + success: async (m) => out(m), + message: async (m) => out(m), + step: async (m) => out(m), + warn: async (m) => err(m), + error: async (m) => err(m) + }; + var notInteractive = () => { + throw new Error("interactive prompt is not available in the ide-sync bundle"); + }; + var identityColor = new Proxy( + {}, + { + get: () => (s) => s + } + ); + module.exports = { + log, + getColor: async () => identityColor, + spinner: () => ({ start() { + }, stop() { + }, message() { + } }), + tasks: async () => { + }, + note: async (m) => out(m), + box: async (m) => out(m), + intro: async () => { + }, + outro: async () => { + }, + cancel: async () => { + }, + handleCancel: async () => { + }, + getClack: notInteractive, + select: notInteractive, + multiselect: notInteractive, + autocomplete: notInteractive, + autocompleteMultiselect: notInteractive, + directory: notInteractive, + confirm: notInteractive, + text: notInteractive, + password: notInteractive, + prompt: notInteractive + }; + } +}); + +// node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = Symbol.for("yaml.alias"); + var DOC = Symbol.for("yaml.document"); + var MAP = Symbol.for("yaml.map"); + var PAIR = Symbol.for("yaml.pair"); + var SCALAR = Symbol.for("yaml.scalar"); + var SEQ = Symbol.for("yaml.seq"); + var NODE_TYPE = Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = Symbol("break visit"); + var SKIP = Symbol("skip children"); + var REMOVE = Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path) { + const ctrl = callVisitor(key, node, visitor, path); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path, ctrl); + return visit_(key, ctrl, visitor, path); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path = Object.freeze(path.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path = Object.freeze(path.concat(node)); + const ck = visit_("key", node.key, visitor, path); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path) { + const ctrl = await callVisitor(key, node, visitor, path); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path, ctrl); + return visitAsync_(key, ctrl, visitor, path); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path = Object.freeze(path.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path = Object.freeze(path.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path) { + if (typeof visitor === "function") + return visitor(key, node, path); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path); + return void 0; + } + function replaceNode(key, path, node) { + const parent = path[path.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path, value) { + let v = value; + for (let i = path.length - 1; i >= 0; --i) { + const k = path[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path) => path == null || typeof path === "object" && !!path[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path, value) { + if (isEmptyPath(path)) + this.add(value); + else { + const [key, ...rest] = path; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path) { + const [key, ...rest] = path; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path, keepScalar) { + const [key, ...rest] = path; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path) { + const [key, ...rest] = path; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path, value) { + const [key, ...rest] = path; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify; + } +}); + +// node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify2 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify2(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path) { + if (Collection.isEmptyPath(path)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path, keepScalar) { + if (Collection.isEmptyPath(path)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path) { + if (Collection.isEmptyPath(path)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path, value) { + if (Collection.isEmptyPath(path)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + Array.prototype.push.apply(doc.errors, this.errors); + Array.prototype.push.apply(doc.warnings, this.warnings); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify; + } +}); + +// node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = Symbol("break visit"); + var SKIP = Symbol("skip children"); + var REMOVE = Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path) => { + let item = cst; + for (const [field, index] of path) { + const tok = item?.[field]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path) => { + const parent = visit.itemAtPath(cst, path.slice(0, -1)); + const field = path[path.length - 1][0]; + const coll = parent?.[field]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path, item, visitor) { + let ctrl = visitor(item, path); + if (typeof ctrl === "symbol") + return ctrl; + for (const field of ["key", "value"]) { + const token = item[field]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field === "key") + ctrl = ctrl(item, path); + } + } + return typeof ctrl === "function" ? ctrl(item, path) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return yield* this.parseBlockStart(); + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + switch (this.charAt(0)) { + case "!": + return (yield* this.pushTag()) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators()); + case "&": + return (yield* this.pushUntil(isNotAnchorChar)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators()); + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + return (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators()); + } + } + } + return 0; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + Array.prototype.push.apply(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + Array.prototype.push.apply(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + Array.prototype.push.apply(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs); + } else { + Object.assign(it, { key: fs, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + Array.prototype.push.apply(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs, sep: [] }); + else if (it.sep) + this.stack.push(fs); + else + Object.assign(it, { key: fs, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument; + exports.stringify = stringify; + } +}); + +// node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// tools/installer/ide/platform-codes.js +var require_platform_codes = __commonJS({ + "tools/installer/ide/platform-codes.js"(exports, module) { + var fs = require_fs_native(); + var path = __require("node:path"); + var yaml = require_dist(); + var _cachedPlatformCodes = null; + function resolvePlatformCodesPath() { + return process.env.BMAD_IDE_PLATFORM_CODES || path.join(__dirname, "platform-codes.yaml"); + } + async function loadPlatformCodes() { + if (_cachedPlatformCodes) { + return _cachedPlatformCodes; + } + const PLATFORM_CODES_PATH = resolvePlatformCodesPath(); + if (!await fs.pathExists(PLATFORM_CODES_PATH)) { + throw new Error(`Platform codes configuration not found at: ${PLATFORM_CODES_PATH}`); + } + const content = await fs.readFile(PLATFORM_CODES_PATH, "utf8"); + _cachedPlatformCodes = yaml.parse(content); + return _cachedPlatformCodes; + } + function clearCache() { + _cachedPlatformCodes = null; + } + async function formatPlatformList() { + const { IdeManager } = require_manager(); + const ideManager = new IdeManager(); + await ideManager.ensureInitialized(); + const entries = ideManager.getAvailableIdes().map((ide) => { + const handler = ideManager.handlers.get(ide.value); + return { + id: ide.value, + name: ide.name, + targetDir: handler?.installerConfig?.target_dir || "", + preferred: ide.preferred + }; + }); + const idWidth = Math.max(...entries.map((e) => e.id.length), "ID".length); + const nameWidth = Math.max(...entries.map((e) => e.name.length), "Name".length); + const pad = (s, w) => s + " ".repeat(Math.max(0, w - s.length)); + const lines = [ + `Supported tool IDs (pass via --tools [,...]):`, + "", + ` ${pad("ID", idWidth)} ${pad("Name", nameWidth)} Target dir`, + ` ${pad("-".repeat(idWidth), idWidth)} ${pad("-".repeat(nameWidth), nameWidth)} ${"-".repeat(10)}` + ]; + for (const e of entries) { + const star = e.preferred ? " *" : " "; + lines.push(`${star}${pad(e.id, idWidth)} ${pad(e.name, nameWidth)} ${e.targetDir}`); + } + lines.push("", "* = recommended / preferred", "", "Example: bmad-method install --modules bmm --tools claude-code"); + return lines.join("\n"); + } + module.exports = { + loadPlatformCodes, + clearCache, + formatPlatformList + }; + } +}); + +// node_modules/csv-parse/dist/cjs/sync.cjs +var require_sync = __commonJS({ + "node_modules/csv-parse/dist/cjs/sync.cjs"(exports) { + "use strict"; + var CsvError = class _CsvError extends Error { + constructor(code, message, options, ...contexts) { + if (Array.isArray(message)) message = message.join(" ").trim(); + super(message); + if (Error.captureStackTrace !== void 0) { + Error.captureStackTrace(this, _CsvError); + } + this.code = code; + for (const context of contexts) { + for (const key in context) { + const value = context[key]; + this[key] = Buffer.isBuffer(value) ? value.toString(options.encoding) : value == null ? value : JSON.parse(JSON.stringify(value)); + } + } + } + }; + var is_object = function(obj) { + return typeof obj === "object" && obj !== null && !Array.isArray(obj); + }; + var normalize_columns_array = function(columns) { + const normalizedColumns = []; + for (let i = 0, l = columns.length; i < l; i++) { + const column = columns[i]; + if (column === void 0 || column === null || column === false) { + normalizedColumns[i] = { disabled: true }; + } else if (typeof column === "string") { + normalizedColumns[i] = { name: column }; + } else if (is_object(column)) { + if (typeof column.name !== "string") { + throw new CsvError("CSV_OPTION_COLUMNS_MISSING_NAME", [ + "Option columns missing name:", + `property "name" is required at position ${i}`, + "when column is an object literal" + ]); + } + normalizedColumns[i] = column; + } else { + throw new CsvError("CSV_INVALID_COLUMN_DEFINITION", [ + "Invalid column definition:", + "expect a string or a literal object,", + `got ${JSON.stringify(column)} at position ${i}` + ]); + } + } + return normalizedColumns; + }; + var ResizeableBuffer = class { + constructor(size = 100) { + this.size = size; + this.length = 0; + this.buf = Buffer.allocUnsafe(size); + } + prepend(val) { + if (Buffer.isBuffer(val)) { + const length = this.length + val.length; + if (length >= this.size) { + this.resize(); + if (length >= this.size) { + throw Error("INVALID_BUFFER_STATE"); + } + } + const buf = this.buf; + this.buf = Buffer.allocUnsafe(this.size); + val.copy(this.buf, 0); + buf.copy(this.buf, val.length); + this.length += val.length; + } else { + const length = this.length++; + if (length === this.size) { + this.resize(); + } + const buf = this.clone(); + this.buf[0] = val; + buf.copy(this.buf, 1, 0, length); + } + } + append(val) { + const length = this.length++; + if (length === this.size) { + this.resize(); + } + this.buf[length] = val; + } + clone() { + return Buffer.from(this.buf.slice(0, this.length)); + } + resize() { + const length = this.length; + this.size = this.size * 2; + const buf = Buffer.allocUnsafe(this.size); + this.buf.copy(buf, 0, 0, length); + this.buf = buf; + } + toString(encoding) { + if (encoding) { + return this.buf.slice(0, this.length).toString(encoding); + } else { + return Uint8Array.prototype.slice.call(this.buf.slice(0, this.length)); + } + } + toJSON() { + return this.toString("utf8"); + } + reset() { + this.length = 0; + } + }; + var np = 12; + var cr$1 = 13; + var nl$1 = 10; + var space = 32; + var tab = 9; + var init_state = function(options) { + return { + bomSkipped: false, + bufBytesStart: 0, + castField: options.cast_function, + commenting: false, + // Current error encountered by a record + error: void 0, + enabled: options.from_line === 1, + escaping: false, + escapeIsQuote: Buffer.isBuffer(options.escape) && Buffer.isBuffer(options.quote) && Buffer.compare(options.escape, options.quote) === 0, + // columns can be `false`, `true`, `Array` + expectedRecordLength: Array.isArray(options.columns) ? options.columns.length : void 0, + field: new ResizeableBuffer(20), + firstLineToHeaders: options.cast_first_line_to_header, + needMoreDataSize: Math.max( + // Skip if the remaining buffer smaller than comment + options.comment !== null ? options.comment.length : 0, + ...options.delimiter.map((delimiter) => delimiter.length), + // Skip if the remaining buffer can be escape sequence + options.quote !== null ? options.quote.length : 0 + ), + previousBuf: void 0, + quoting: false, + stop: false, + rawBuffer: new ResizeableBuffer(100), + record: [], + recordHasError: false, + record_length: 0, + recordDelimiterMaxLength: options.record_delimiter.length === 0 ? 0 : Math.max(...options.record_delimiter.map((v) => v.length)), + trimChars: [ + Buffer.from(" ", options.encoding)[0], + Buffer.from(" ", options.encoding)[0] + ], + wasQuoting: false, + wasRowDelimiter: false, + timchars: [ + Buffer.from(Buffer.from([cr$1], "utf8").toString(), options.encoding), + Buffer.from(Buffer.from([nl$1], "utf8").toString(), options.encoding), + Buffer.from(Buffer.from([np], "utf8").toString(), options.encoding), + Buffer.from(Buffer.from([space], "utf8").toString(), options.encoding), + Buffer.from(Buffer.from([tab], "utf8").toString(), options.encoding) + ] + }; + }; + var underscore = function(str) { + return str.replace(/([A-Z])/g, function(_, match) { + return "_" + match.toLowerCase(); + }); + }; + var normalize_options = function(opts) { + const options = {}; + for (const opt in opts) { + options[underscore(opt)] = opts[opt]; + } + if (options.encoding === void 0 || options.encoding === true) { + options.encoding = "utf8"; + } else if (options.encoding === null || options.encoding === false) { + options.encoding = null; + } else if (typeof options.encoding !== "string" && options.encoding !== null) { + throw new CsvError( + "CSV_INVALID_OPTION_ENCODING", + [ + "Invalid option encoding:", + "encoding must be a string or null to return a buffer,", + `got ${JSON.stringify(options.encoding)}` + ], + options + ); + } + if (options.bom === void 0 || options.bom === null || options.bom === false) { + options.bom = false; + } else if (options.bom !== true) { + throw new CsvError( + "CSV_INVALID_OPTION_BOM", + [ + "Invalid option bom:", + "bom must be true,", + `got ${JSON.stringify(options.bom)}` + ], + options + ); + } + options.cast_function = null; + if (options.cast === void 0 || options.cast === null || options.cast === false || options.cast === "") { + options.cast = void 0; + } else if (typeof options.cast === "function") { + options.cast_function = options.cast; + options.cast = true; + } else if (options.cast !== true) { + throw new CsvError( + "CSV_INVALID_OPTION_CAST", + [ + "Invalid option cast:", + "cast must be true or a function,", + `got ${JSON.stringify(options.cast)}` + ], + options + ); + } + if (options.cast_date === void 0 || options.cast_date === null || options.cast_date === false || options.cast_date === "") { + options.cast_date = false; + } else if (options.cast_date === true) { + options.cast_date = function(value) { + const date = Date.parse(value); + return !isNaN(date) ? new Date(date) : value; + }; + } else if (typeof options.cast_date !== "function") { + throw new CsvError( + "CSV_INVALID_OPTION_CAST_DATE", + [ + "Invalid option cast_date:", + "cast_date must be true or a function,", + `got ${JSON.stringify(options.cast_date)}` + ], + options + ); + } + options.cast_first_line_to_header = void 0; + if (options.columns === true) { + options.cast_first_line_to_header = void 0; + } else if (typeof options.columns === "function") { + options.cast_first_line_to_header = options.columns; + options.columns = true; + } else if (Array.isArray(options.columns)) { + options.columns = normalize_columns_array(options.columns); + } else if (options.columns === void 0 || options.columns === null || options.columns === false) { + options.columns = false; + } else { + throw new CsvError( + "CSV_INVALID_OPTION_COLUMNS", + [ + "Invalid option columns:", + "expect an array, a function or true,", + `got ${JSON.stringify(options.columns)}` + ], + options + ); + } + if (options.group_columns_by_name === void 0 || options.group_columns_by_name === null || options.group_columns_by_name === false) { + options.group_columns_by_name = false; + } else if (options.group_columns_by_name !== true) { + throw new CsvError( + "CSV_INVALID_OPTION_GROUP_COLUMNS_BY_NAME", + [ + "Invalid option group_columns_by_name:", + "expect an boolean,", + `got ${JSON.stringify(options.group_columns_by_name)}` + ], + options + ); + } else if (options.columns === false) { + throw new CsvError( + "CSV_INVALID_OPTION_GROUP_COLUMNS_BY_NAME", + [ + "Invalid option group_columns_by_name:", + "the `columns` mode must be activated." + ], + options + ); + } + if (options.comment === void 0 || options.comment === null || options.comment === false || options.comment === "") { + options.comment = null; + } else { + if (typeof options.comment === "string") { + options.comment = Buffer.from(options.comment, options.encoding); + } + if (!Buffer.isBuffer(options.comment)) { + throw new CsvError( + "CSV_INVALID_OPTION_COMMENT", + [ + "Invalid option comment:", + "comment must be a buffer or a string,", + `got ${JSON.stringify(options.comment)}` + ], + options + ); + } + } + if (options.comment_no_infix === void 0 || options.comment_no_infix === null || options.comment_no_infix === false) { + options.comment_no_infix = false; + } else if (options.comment_no_infix !== true) { + throw new CsvError( + "CSV_INVALID_OPTION_COMMENT", + [ + "Invalid option comment_no_infix:", + "value must be a boolean,", + `got ${JSON.stringify(options.comment_no_infix)}` + ], + options + ); + } + const delimiter_json = JSON.stringify(options.delimiter); + if (!Array.isArray(options.delimiter)) + options.delimiter = [options.delimiter]; + if (options.delimiter.length === 0) { + throw new CsvError( + "CSV_INVALID_OPTION_DELIMITER", + [ + "Invalid option delimiter:", + "delimiter must be a non empty string or buffer or array of string|buffer,", + `got ${delimiter_json}` + ], + options + ); + } + options.delimiter = options.delimiter.map(function(delimiter) { + if (delimiter === void 0 || delimiter === null || delimiter === false) { + return Buffer.from(",", options.encoding); + } + if (typeof delimiter === "string") { + delimiter = Buffer.from(delimiter, options.encoding); + } + if (!Buffer.isBuffer(delimiter) || delimiter.length === 0) { + throw new CsvError( + "CSV_INVALID_OPTION_DELIMITER", + [ + "Invalid option delimiter:", + "delimiter must be a non empty string or buffer or array of string|buffer,", + `got ${delimiter_json}` + ], + options + ); + } + return delimiter; + }); + if (options.escape === void 0 || options.escape === true) { + options.escape = Buffer.from('"', options.encoding); + } else if (typeof options.escape === "string") { + options.escape = Buffer.from(options.escape, options.encoding); + } else if (options.escape === null || options.escape === false) { + options.escape = null; + } + if (options.escape !== null) { + if (!Buffer.isBuffer(options.escape)) { + throw new Error( + `Invalid Option: escape must be a buffer, a string or a boolean, got ${JSON.stringify(options.escape)}` + ); + } + } + if (options.from === void 0 || options.from === null) { + options.from = 1; + } else { + if (typeof options.from === "string" && /\d+/.test(options.from)) { + options.from = parseInt(options.from); + } + if (Number.isInteger(options.from)) { + if (options.from < 0) { + throw new Error( + `Invalid Option: from must be a positive integer, got ${JSON.stringify(opts.from)}` + ); + } + } else { + throw new Error( + `Invalid Option: from must be an integer, got ${JSON.stringify(options.from)}` + ); + } + } + if (options.from_line === void 0 || options.from_line === null) { + options.from_line = 1; + } else { + if (typeof options.from_line === "string" && /\d+/.test(options.from_line)) { + options.from_line = parseInt(options.from_line); + } + if (Number.isInteger(options.from_line)) { + if (options.from_line <= 0) { + throw new Error( + `Invalid Option: from_line must be a positive integer greater than 0, got ${JSON.stringify(opts.from_line)}` + ); + } + } else { + throw new Error( + `Invalid Option: from_line must be an integer, got ${JSON.stringify(opts.from_line)}` + ); + } + } + if (options.ignore_last_delimiters === void 0 || options.ignore_last_delimiters === null) { + options.ignore_last_delimiters = false; + } else if (typeof options.ignore_last_delimiters === "number") { + options.ignore_last_delimiters = Math.floor(options.ignore_last_delimiters); + if (options.ignore_last_delimiters === 0) { + options.ignore_last_delimiters = false; + } + } else if (typeof options.ignore_last_delimiters !== "boolean") { + throw new CsvError( + "CSV_INVALID_OPTION_IGNORE_LAST_DELIMITERS", + [ + "Invalid option `ignore_last_delimiters`:", + "the value must be a boolean value or an integer,", + `got ${JSON.stringify(options.ignore_last_delimiters)}` + ], + options + ); + } + if (options.ignore_last_delimiters === true && options.columns === false) { + throw new CsvError( + "CSV_IGNORE_LAST_DELIMITERS_REQUIRES_COLUMNS", + [ + "The option `ignore_last_delimiters`", + "requires the activation of the `columns` option" + ], + options + ); + } + if (options.info === void 0 || options.info === null || options.info === false) { + options.info = false; + } else if (options.info !== true) { + throw new Error( + `Invalid Option: info must be true, got ${JSON.stringify(options.info)}` + ); + } + if (options.max_record_size === void 0 || options.max_record_size === null || options.max_record_size === false) { + options.max_record_size = 0; + } else if (Number.isInteger(options.max_record_size) && options.max_record_size >= 0) ; + else if (typeof options.max_record_size === "string" && /\d+/.test(options.max_record_size)) { + options.max_record_size = parseInt(options.max_record_size); + } else { + throw new Error( + `Invalid Option: max_record_size must be a positive integer, got ${JSON.stringify(options.max_record_size)}` + ); + } + if (options.objname === void 0 || options.objname === null || options.objname === false) { + options.objname = void 0; + } else if (Buffer.isBuffer(options.objname)) { + if (options.objname.length === 0) { + throw new Error(`Invalid Option: objname must be a non empty buffer`); + } + if (options.encoding === null) ; + else { + options.objname = options.objname.toString(options.encoding); + } + } else if (typeof options.objname === "string") { + if (options.objname.length === 0) { + throw new Error(`Invalid Option: objname must be a non empty string`); + } + } else if (typeof options.objname === "number") ; + else { + throw new Error( + `Invalid Option: objname must be a string or a buffer, got ${options.objname}` + ); + } + if (options.objname !== void 0) { + if (typeof options.objname === "number") { + if (options.columns !== false) { + throw Error( + "Invalid Option: objname index cannot be combined with columns or be defined as a field" + ); + } + } else { + if (options.columns === false) { + throw Error( + "Invalid Option: objname field must be combined with columns or be defined as an index" + ); + } + } + } + if (options.on_record === void 0 || options.on_record === null) { + options.on_record = void 0; + } else if (typeof options.on_record !== "function") { + throw new CsvError( + "CSV_INVALID_OPTION_ON_RECORD", + [ + "Invalid option `on_record`:", + "expect a function,", + `got ${JSON.stringify(options.on_record)}` + ], + options + ); + } + if (options.on_skip !== void 0 && options.on_skip !== null && typeof options.on_skip !== "function") { + throw new Error( + `Invalid Option: on_skip must be a function, got ${JSON.stringify(options.on_skip)}` + ); + } + if (options.quote === null || options.quote === false || options.quote === "") { + options.quote = null; + } else { + if (options.quote === void 0 || options.quote === true) { + options.quote = Buffer.from('"', options.encoding); + } else if (typeof options.quote === "string") { + options.quote = Buffer.from(options.quote, options.encoding); + } + if (!Buffer.isBuffer(options.quote)) { + throw new Error( + `Invalid Option: quote must be a buffer or a string, got ${JSON.stringify(options.quote)}` + ); + } + } + if (options.raw === void 0 || options.raw === null || options.raw === false) { + options.raw = false; + } else if (options.raw !== true) { + throw new Error( + `Invalid Option: raw must be true, got ${JSON.stringify(options.raw)}` + ); + } + if (options.record_delimiter === void 0) { + options.record_delimiter = []; + } else if (typeof options.record_delimiter === "string" || Buffer.isBuffer(options.record_delimiter)) { + if (options.record_delimiter.length === 0) { + throw new CsvError( + "CSV_INVALID_OPTION_RECORD_DELIMITER", + [ + "Invalid option `record_delimiter`:", + "value must be a non empty string or buffer,", + `got ${JSON.stringify(options.record_delimiter)}` + ], + options + ); + } + options.record_delimiter = [options.record_delimiter]; + } else if (!Array.isArray(options.record_delimiter)) { + throw new CsvError( + "CSV_INVALID_OPTION_RECORD_DELIMITER", + [ + "Invalid option `record_delimiter`:", + "value must be a string, a buffer or array of string|buffer,", + `got ${JSON.stringify(options.record_delimiter)}` + ], + options + ); + } + options.record_delimiter = options.record_delimiter.map(function(rd, i) { + if (typeof rd !== "string" && !Buffer.isBuffer(rd)) { + throw new CsvError( + "CSV_INVALID_OPTION_RECORD_DELIMITER", + [ + "Invalid option `record_delimiter`:", + "value must be a string, a buffer or array of string|buffer", + `at index ${i},`, + `got ${JSON.stringify(rd)}` + ], + options + ); + } else if (rd.length === 0) { + throw new CsvError( + "CSV_INVALID_OPTION_RECORD_DELIMITER", + [ + "Invalid option `record_delimiter`:", + "value must be a non empty string or buffer", + `at index ${i},`, + `got ${JSON.stringify(rd)}` + ], + options + ); + } + if (typeof rd === "string") { + rd = Buffer.from(rd, options.encoding); + } + return rd; + }); + if (typeof options.relax_column_count === "boolean") ; + else if (options.relax_column_count === void 0 || options.relax_column_count === null) { + options.relax_column_count = false; + } else { + throw new Error( + `Invalid Option: relax_column_count must be a boolean, got ${JSON.stringify(options.relax_column_count)}` + ); + } + if (typeof options.relax_column_count_less === "boolean") ; + else if (options.relax_column_count_less === void 0 || options.relax_column_count_less === null) { + options.relax_column_count_less = false; + } else { + throw new Error( + `Invalid Option: relax_column_count_less must be a boolean, got ${JSON.stringify(options.relax_column_count_less)}` + ); + } + if (typeof options.relax_column_count_more === "boolean") ; + else if (options.relax_column_count_more === void 0 || options.relax_column_count_more === null) { + options.relax_column_count_more = false; + } else { + throw new Error( + `Invalid Option: relax_column_count_more must be a boolean, got ${JSON.stringify(options.relax_column_count_more)}` + ); + } + if (typeof options.relax_quotes === "boolean") ; + else if (options.relax_quotes === void 0 || options.relax_quotes === null) { + options.relax_quotes = false; + } else { + throw new Error( + `Invalid Option: relax_quotes must be a boolean, got ${JSON.stringify(options.relax_quotes)}` + ); + } + if (typeof options.skip_empty_lines === "boolean") ; + else if (options.skip_empty_lines === void 0 || options.skip_empty_lines === null) { + options.skip_empty_lines = false; + } else { + throw new Error( + `Invalid Option: skip_empty_lines must be a boolean, got ${JSON.stringify(options.skip_empty_lines)}` + ); + } + if (typeof options.skip_records_with_empty_values === "boolean") ; + else if (options.skip_records_with_empty_values === void 0 || options.skip_records_with_empty_values === null) { + options.skip_records_with_empty_values = false; + } else { + throw new Error( + `Invalid Option: skip_records_with_empty_values must be a boolean, got ${JSON.stringify(options.skip_records_with_empty_values)}` + ); + } + if (typeof options.skip_records_with_error === "boolean") ; + else if (options.skip_records_with_error === void 0 || options.skip_records_with_error === null) { + options.skip_records_with_error = false; + } else { + throw new Error( + `Invalid Option: skip_records_with_error must be a boolean, got ${JSON.stringify(options.skip_records_with_error)}` + ); + } + if (options.rtrim === void 0 || options.rtrim === null || options.rtrim === false) { + options.rtrim = false; + } else if (options.rtrim !== true) { + throw new Error( + `Invalid Option: rtrim must be a boolean, got ${JSON.stringify(options.rtrim)}` + ); + } + if (options.ltrim === void 0 || options.ltrim === null || options.ltrim === false) { + options.ltrim = false; + } else if (options.ltrim !== true) { + throw new Error( + `Invalid Option: ltrim must be a boolean, got ${JSON.stringify(options.ltrim)}` + ); + } + if (options.trim === void 0 || options.trim === null || options.trim === false) { + options.trim = false; + } else if (options.trim !== true) { + throw new Error( + `Invalid Option: trim must be a boolean, got ${JSON.stringify(options.trim)}` + ); + } + if (options.trim === true && opts.ltrim !== false) { + options.ltrim = true; + } else if (options.ltrim !== true) { + options.ltrim = false; + } + if (options.trim === true && opts.rtrim !== false) { + options.rtrim = true; + } else if (options.rtrim !== true) { + options.rtrim = false; + } + if (options.to === void 0 || options.to === null) { + options.to = -1; + } else if (options.to !== -1) { + if (typeof options.to === "string" && /\d+/.test(options.to)) { + options.to = parseInt(options.to); + } + if (Number.isInteger(options.to)) { + if (options.to <= 0) { + throw new Error( + `Invalid Option: to must be a positive integer greater than 0, got ${JSON.stringify(opts.to)}` + ); + } + } else { + throw new Error( + `Invalid Option: to must be an integer, got ${JSON.stringify(opts.to)}` + ); + } + } + if (options.to_line === void 0 || options.to_line === null) { + options.to_line = -1; + } else if (options.to_line !== -1) { + if (typeof options.to_line === "string" && /\d+/.test(options.to_line)) { + options.to_line = parseInt(options.to_line); + } + if (Number.isInteger(options.to_line)) { + if (options.to_line <= 0) { + throw new Error( + `Invalid Option: to_line must be a positive integer greater than 0, got ${JSON.stringify(opts.to_line)}` + ); + } + } else { + throw new Error( + `Invalid Option: to_line must be an integer, got ${JSON.stringify(opts.to_line)}` + ); + } + } + return options; + }; + var isRecordEmpty = function(record) { + return record.every( + (field) => field == null || field.toString && field.toString().trim() === "" + ); + }; + var cr = 13; + var nl = 10; + var boms = { + // Note, the following are equals: + // Buffer.from("\ufeff") + // Buffer.from([239, 187, 191]) + // Buffer.from('EFBBBF', 'hex') + utf8: Buffer.from([239, 187, 191]), + // Note, the following are equals: + // Buffer.from "\ufeff", 'utf16le + // Buffer.from([255, 254]) + utf16le: Buffer.from([255, 254]) + }; + var transform = function(original_options = {}) { + const info = { + bytes: 0, + comment_lines: 0, + empty_lines: 0, + invalid_field_length: 0, + lines: 1, + records: 0 + }; + const options = normalize_options(original_options); + return { + info, + original_options, + options, + state: init_state(options), + __needMoreData: function(i, bufLen, end) { + if (end) return false; + const { encoding, escape, quote } = this.options; + const { quoting, needMoreDataSize, recordDelimiterMaxLength } = this.state; + const numOfCharLeft = bufLen - i - 1; + const requiredLength = Math.max( + needMoreDataSize, + // Skip if the remaining buffer smaller than record delimiter + // If "record_delimiter" is yet to be discovered: + // 1. It is equals to `[]` and "recordDelimiterMaxLength" equals `0` + // 2. We set the length to windows line ending in the current encoding + // Note, that encoding is known from user or bom discovery at that point + // recordDelimiterMaxLength, + recordDelimiterMaxLength === 0 ? Buffer.from("\r\n", encoding).length : recordDelimiterMaxLength, + // Skip if remaining buffer can be an escaped quote + quoting ? (escape === null ? 0 : escape.length) + quote.length : 0, + // Skip if remaining buffer can be record delimiter following the closing quote + quoting ? quote.length + recordDelimiterMaxLength : 0 + ); + return numOfCharLeft < requiredLength; + }, + // Central parser implementation + parse: function(nextBuf, end, push, close) { + const { + bom, + comment_no_infix, + encoding, + from_line, + ltrim, + max_record_size, + raw, + relax_quotes, + rtrim, + skip_empty_lines, + to, + to_line + } = this.options; + let { comment, escape, quote, record_delimiter } = this.options; + const { bomSkipped, previousBuf, rawBuffer, escapeIsQuote } = this.state; + let buf; + if (previousBuf === void 0) { + if (nextBuf === void 0) { + close(); + return; + } else { + buf = nextBuf; + } + } else if (previousBuf !== void 0 && nextBuf === void 0) { + buf = previousBuf; + } else { + buf = Buffer.concat([previousBuf, nextBuf]); + } + if (bomSkipped === false) { + if (bom === false) { + this.state.bomSkipped = true; + } else if (buf.length < 3) { + if (end === false) { + this.state.previousBuf = buf; + return; + } + } else { + for (const encoding2 in boms) { + if (boms[encoding2].compare(buf, 0, boms[encoding2].length) === 0) { + const bomLength = boms[encoding2].length; + this.state.bufBytesStart += bomLength; + buf = buf.slice(bomLength); + const options2 = normalize_options({ + ...this.original_options, + encoding: encoding2 + }); + for (const key in options2) { + this.options[key] = options2[key]; + } + ({ comment, escape, quote } = this.options); + break; + } + } + this.state.bomSkipped = true; + } + } + const bufLen = buf.length; + let pos; + for (pos = 0; pos < bufLen; pos++) { + if (this.__needMoreData(pos, bufLen, end)) { + break; + } + if (this.state.wasRowDelimiter === true) { + this.info.lines++; + this.state.wasRowDelimiter = false; + } + if (to_line !== -1 && this.info.lines > to_line) { + this.state.stop = true; + close(); + return; + } + if (this.state.quoting === false && record_delimiter.length === 0) { + const record_delimiterCount = this.__autoDiscoverRecordDelimiter( + buf, + pos + ); + if (record_delimiterCount) { + record_delimiter = this.options.record_delimiter; + } + } + const chr = buf[pos]; + if (raw === true) { + rawBuffer.append(chr); + } + if ((chr === cr || chr === nl) && this.state.wasRowDelimiter === false) { + this.state.wasRowDelimiter = true; + } + if (this.state.escaping === true) { + this.state.escaping = false; + } else { + if (escape !== null && this.state.quoting === true && this.__isEscape(buf, pos, chr) && pos + escape.length < bufLen) { + if (escapeIsQuote) { + if (this.__isQuote(buf, pos + escape.length)) { + this.state.escaping = true; + pos += escape.length - 1; + continue; + } + } else { + this.state.escaping = true; + pos += escape.length - 1; + continue; + } + } + if (this.state.commenting === false && this.__isQuote(buf, pos)) { + if (this.state.quoting === true) { + const nextChr = buf[pos + quote.length]; + const isNextChrTrimable = rtrim && this.__isCharTrimable(buf, pos + quote.length); + const isNextChrComment = comment !== null && this.__compareBytes(comment, buf, pos + quote.length, nextChr); + const isNextChrDelimiter = this.__isDelimiter( + buf, + pos + quote.length, + nextChr + ); + const isNextChrRecordDelimiter = record_delimiter.length === 0 ? this.__autoDiscoverRecordDelimiter(buf, pos + quote.length) : this.__isRecordDelimiter(nextChr, buf, pos + quote.length); + if (escape !== null && this.__isEscape(buf, pos, chr) && this.__isQuote(buf, pos + escape.length)) { + pos += escape.length - 1; + } else if (!nextChr || isNextChrDelimiter || isNextChrRecordDelimiter || isNextChrComment || isNextChrTrimable) { + this.state.quoting = false; + this.state.wasQuoting = true; + pos += quote.length - 1; + continue; + } else if (relax_quotes === false) { + const err = this.__error( + new CsvError( + "CSV_INVALID_CLOSING_QUOTE", + [ + "Invalid Closing Quote:", + `got "${String.fromCharCode(nextChr)}"`, + `at line ${this.info.lines}`, + "instead of delimiter, record delimiter, trimable character", + "(if activated) or comment" + ], + this.options, + this.__infoField() + ) + ); + if (err !== void 0) return err; + } else { + this.state.quoting = false; + this.state.wasQuoting = true; + this.state.field.prepend(quote); + pos += quote.length - 1; + } + } else { + if (this.state.field.length !== 0) { + if (relax_quotes === false) { + const info2 = this.__infoField(); + const bom2 = Object.keys(boms).map( + (b) => boms[b].equals(this.state.field.toString()) ? b : false + ).filter(Boolean)[0]; + const err = this.__error( + new CsvError( + "INVALID_OPENING_QUOTE", + [ + "Invalid Opening Quote:", + `a quote is found on field ${JSON.stringify(info2.column)} at line ${info2.lines}, value is ${JSON.stringify(this.state.field.toString(encoding))}`, + bom2 ? `(${bom2} bom)` : void 0 + ], + this.options, + info2, + { + field: this.state.field + } + ) + ); + if (err !== void 0) return err; + } + } else { + this.state.quoting = true; + pos += quote.length - 1; + continue; + } + } + } + if (this.state.quoting === false) { + const recordDelimiterLength = this.__isRecordDelimiter( + chr, + buf, + pos + ); + if (recordDelimiterLength !== 0) { + const skipCommentLine = this.state.commenting && this.state.wasQuoting === false && this.state.record.length === 0 && this.state.field.length === 0; + if (skipCommentLine) { + this.info.comment_lines++; + } else { + if (this.state.enabled === false && this.info.lines + (this.state.wasRowDelimiter === true ? 1 : 0) >= from_line) { + this.state.enabled = true; + this.__resetField(); + this.__resetRecord(); + pos += recordDelimiterLength - 1; + continue; + } + if (skip_empty_lines === true && this.state.wasQuoting === false && this.state.record.length === 0 && this.state.field.length === 0) { + this.info.empty_lines++; + pos += recordDelimiterLength - 1; + continue; + } + this.info.bytes = this.state.bufBytesStart + pos; + const errField = this.__onField(); + if (errField !== void 0) return errField; + this.info.bytes = this.state.bufBytesStart + pos + recordDelimiterLength; + const errRecord = this.__onRecord(push); + if (errRecord !== void 0) return errRecord; + if (to !== -1 && this.info.records >= to) { + this.state.stop = true; + close(); + return; + } + } + this.state.commenting = false; + pos += recordDelimiterLength - 1; + continue; + } + if (this.state.commenting) { + continue; + } + if (comment !== null && (comment_no_infix === false || this.state.record.length === 0 && this.state.field.length === 0)) { + const commentCount = this.__compareBytes(comment, buf, pos, chr); + if (commentCount !== 0) { + this.state.commenting = true; + continue; + } + } + const delimiterLength = this.__isDelimiter(buf, pos, chr); + if (delimiterLength !== 0) { + this.info.bytes = this.state.bufBytesStart + pos; + const errField = this.__onField(); + if (errField !== void 0) return errField; + pos += delimiterLength - 1; + continue; + } + } + } + if (this.state.commenting === false) { + if (max_record_size !== 0 && this.state.record_length + this.state.field.length > max_record_size) { + return this.__error( + new CsvError( + "CSV_MAX_RECORD_SIZE", + [ + "Max Record Size:", + "record exceed the maximum number of tolerated bytes", + `of ${max_record_size}`, + `at line ${this.info.lines}` + ], + this.options, + this.__infoField() + ) + ); + } + } + const lappend = ltrim === false || this.state.quoting === true || this.state.field.length !== 0 || !this.__isCharTrimable(buf, pos); + const rappend = rtrim === false || this.state.wasQuoting === false; + if (lappend === true && rappend === true) { + this.state.field.append(chr); + } else if (rtrim === true && !this.__isCharTrimable(buf, pos)) { + return this.__error( + new CsvError( + "CSV_NON_TRIMABLE_CHAR_AFTER_CLOSING_QUOTE", + [ + "Invalid Closing Quote:", + "found non trimable byte after quote", + `at line ${this.info.lines}` + ], + this.options, + this.__infoField() + ) + ); + } else { + if (lappend === false) { + pos += this.__isCharTrimable(buf, pos) - 1; + } + continue; + } + } + if (end === true) { + if (this.state.quoting === true) { + const err = this.__error( + new CsvError( + "CSV_QUOTE_NOT_CLOSED", + [ + "Quote Not Closed:", + `the parsing is finished with an opening quote at line ${this.info.lines}` + ], + this.options, + this.__infoField() + ) + ); + if (err !== void 0) return err; + } else { + if (this.state.wasQuoting === true || this.state.record.length !== 0 || this.state.field.length !== 0) { + this.info.bytes = this.state.bufBytesStart + pos; + const errField = this.__onField(); + if (errField !== void 0) return errField; + const errRecord = this.__onRecord(push); + if (errRecord !== void 0) return errRecord; + } else if (this.state.wasRowDelimiter === true) { + this.info.empty_lines++; + } else if (this.state.commenting === true) { + this.info.comment_lines++; + } + } + } else { + this.state.bufBytesStart += pos; + this.state.previousBuf = buf.slice(pos); + } + if (this.state.wasRowDelimiter === true) { + this.info.lines++; + this.state.wasRowDelimiter = false; + } + }, + __onRecord: function(push) { + const { + columns, + group_columns_by_name, + encoding, + info: info2, + from, + relax_column_count, + relax_column_count_less, + relax_column_count_more, + raw, + skip_records_with_empty_values + } = this.options; + const { enabled, record } = this.state; + if (enabled === false) { + return this.__resetRecord(); + } + const recordLength = record.length; + if (columns === true) { + if (skip_records_with_empty_values === true && isRecordEmpty(record)) { + this.__resetRecord(); + return; + } + return this.__firstLineToColumns(record); + } + if (columns === false && this.info.records === 0) { + this.state.expectedRecordLength = recordLength; + } + if (recordLength !== this.state.expectedRecordLength) { + const err = columns === false ? new CsvError( + "CSV_RECORD_INCONSISTENT_FIELDS_LENGTH", + [ + "Invalid Record Length:", + `expect ${this.state.expectedRecordLength},`, + `got ${recordLength} on line ${this.info.lines}` + ], + this.options, + this.__infoField(), + { + record + } + ) : new CsvError( + "CSV_RECORD_INCONSISTENT_COLUMNS", + [ + "Invalid Record Length:", + `columns length is ${columns.length},`, + // rename columns + `got ${recordLength} on line ${this.info.lines}` + ], + this.options, + this.__infoField(), + { + record + } + ); + if (relax_column_count === true || relax_column_count_less === true && recordLength < this.state.expectedRecordLength || relax_column_count_more === true && recordLength > this.state.expectedRecordLength) { + this.info.invalid_field_length++; + this.state.error = err; + } else { + const finalErr = this.__error(err); + if (finalErr) return finalErr; + } + } + if (skip_records_with_empty_values === true && isRecordEmpty(record)) { + this.__resetRecord(); + return; + } + if (this.state.recordHasError === true) { + this.__resetRecord(); + this.state.recordHasError = false; + return; + } + this.info.records++; + if (from === 1 || this.info.records >= from) { + const { objname } = this.options; + if (columns !== false) { + const obj = {}; + for (let i = 0, l = record.length; i < l; i++) { + if (columns[i] === void 0 || columns[i].disabled) continue; + if (group_columns_by_name === true && obj[columns[i].name] !== void 0) { + if (Array.isArray(obj[columns[i].name])) { + obj[columns[i].name] = obj[columns[i].name].concat(record[i]); + } else { + obj[columns[i].name] = [obj[columns[i].name], record[i]]; + } + } else { + obj[columns[i].name] = record[i]; + } + } + if (raw === true || info2 === true) { + const extRecord = Object.assign( + { record: obj }, + raw === true ? { raw: this.state.rawBuffer.toString(encoding) } : {}, + info2 === true ? { info: this.__infoRecord() } : {} + ); + const err = this.__push( + objname === void 0 ? extRecord : [obj[objname], extRecord], + push + ); + if (err) { + return err; + } + } else { + const err = this.__push( + objname === void 0 ? obj : [obj[objname], obj], + push + ); + if (err) { + return err; + } + } + } else { + if (raw === true || info2 === true) { + const extRecord = Object.assign( + { record }, + raw === true ? { raw: this.state.rawBuffer.toString(encoding) } : {}, + info2 === true ? { info: this.__infoRecord() } : {} + ); + const err = this.__push( + objname === void 0 ? extRecord : [record[objname], extRecord], + push + ); + if (err) { + return err; + } + } else { + const err = this.__push( + objname === void 0 ? record : [record[objname], record], + push + ); + if (err) { + return err; + } + } + } + } + this.__resetRecord(); + }, + __firstLineToColumns: function(record) { + const { firstLineToHeaders } = this.state; + try { + const headers = firstLineToHeaders === void 0 ? record : firstLineToHeaders.call(null, record); + if (!Array.isArray(headers)) { + return this.__error( + new CsvError( + "CSV_INVALID_COLUMN_MAPPING", + [ + "Invalid Column Mapping:", + "expect an array from column function,", + `got ${JSON.stringify(headers)}` + ], + this.options, + this.__infoField(), + { + headers + } + ) + ); + } + const normalizedHeaders = normalize_columns_array(headers); + this.state.expectedRecordLength = normalizedHeaders.length; + this.options.columns = normalizedHeaders; + this.__resetRecord(); + return; + } catch (err) { + return err; + } + }, + __resetRecord: function() { + if (this.options.raw === true) { + this.state.rawBuffer.reset(); + } + this.state.error = void 0; + this.state.record = []; + this.state.record_length = 0; + }, + __onField: function() { + const { cast, encoding, rtrim, max_record_size } = this.options; + const { enabled, wasQuoting } = this.state; + if (enabled === false) { + return this.__resetField(); + } + let field = this.state.field.toString(encoding); + if (rtrim === true && wasQuoting === false) { + field = field.trimRight(); + } + if (cast === true) { + const [err, f] = this.__cast(field); + if (err !== void 0) return err; + field = f; + } + this.state.record.push(field); + if (max_record_size !== 0 && typeof field === "string") { + this.state.record_length += field.length; + } + this.__resetField(); + }, + __resetField: function() { + this.state.field.reset(); + this.state.wasQuoting = false; + }, + __push: function(record, push) { + const { on_record } = this.options; + if (on_record !== void 0) { + const info2 = this.__infoRecord(); + try { + record = on_record.call(null, record, info2); + } catch (err) { + return err; + } + if (record === void 0 || record === null) { + return; + } + } + push(record); + }, + // Return a tuple with the error and the casted value + __cast: function(field) { + const { columns, relax_column_count } = this.options; + const isColumns = Array.isArray(columns); + if (isColumns === true && relax_column_count && this.options.columns.length <= this.state.record.length) { + return [void 0, void 0]; + } + if (this.state.castField !== null) { + try { + const info2 = this.__infoField(); + return [void 0, this.state.castField.call(null, field, info2)]; + } catch (err) { + return [err]; + } + } + if (this.__isFloat(field)) { + return [void 0, parseFloat(field)]; + } else if (this.options.cast_date !== false) { + const info2 = this.__infoField(); + return [void 0, this.options.cast_date.call(null, field, info2)]; + } + return [void 0, field]; + }, + // Helper to test if a character is a space or a line delimiter + __isCharTrimable: function(buf, pos) { + const isTrim = (buf2, pos2) => { + const { timchars } = this.state; + loop1: for (let i = 0; i < timchars.length; i++) { + const timchar = timchars[i]; + for (let j = 0; j < timchar.length; j++) { + if (timchar[j] !== buf2[pos2 + j]) continue loop1; + } + return timchar.length; + } + return 0; + }; + return isTrim(buf, pos); + }, + // Keep it in case we implement the `cast_int` option + // __isInt(value){ + // // return Number.isInteger(parseInt(value)) + // // return !isNaN( parseInt( obj ) ); + // return /^(\-|\+)?[1-9][0-9]*$/.test(value) + // } + __isFloat: function(value) { + return value - parseFloat(value) + 1 >= 0; + }, + __compareBytes: function(sourceBuf, targetBuf, targetPos, firstByte) { + if (sourceBuf[0] !== firstByte) return 0; + const sourceLength = sourceBuf.length; + for (let i = 1; i < sourceLength; i++) { + if (sourceBuf[i] !== targetBuf[targetPos + i]) return 0; + } + return sourceLength; + }, + __isDelimiter: function(buf, pos, chr) { + const { delimiter, ignore_last_delimiters } = this.options; + if (ignore_last_delimiters === true && this.state.record.length === this.options.columns.length - 1) { + return 0; + } else if (ignore_last_delimiters !== false && typeof ignore_last_delimiters === "number" && this.state.record.length === ignore_last_delimiters - 1) { + return 0; + } + loop1: for (let i = 0; i < delimiter.length; i++) { + const del = delimiter[i]; + if (del[0] === chr) { + for (let j = 1; j < del.length; j++) { + if (del[j] !== buf[pos + j]) continue loop1; + } + return del.length; + } + } + return 0; + }, + __isRecordDelimiter: function(chr, buf, pos) { + const { record_delimiter } = this.options; + const recordDelimiterLength = record_delimiter.length; + loop1: for (let i = 0; i < recordDelimiterLength; i++) { + const rd = record_delimiter[i]; + const rdLength = rd.length; + if (rd[0] !== chr) { + continue; + } + for (let j = 1; j < rdLength; j++) { + if (rd[j] !== buf[pos + j]) { + continue loop1; + } + } + return rd.length; + } + return 0; + }, + __isEscape: function(buf, pos, chr) { + const { escape } = this.options; + if (escape === null) return false; + const l = escape.length; + if (escape[0] === chr) { + for (let i = 0; i < l; i++) { + if (escape[i] !== buf[pos + i]) { + return false; + } + } + return true; + } + return false; + }, + __isQuote: function(buf, pos) { + const { quote } = this.options; + if (quote === null) return false; + const l = quote.length; + for (let i = 0; i < l; i++) { + if (quote[i] !== buf[pos + i]) { + return false; + } + } + return true; + }, + __autoDiscoverRecordDelimiter: function(buf, pos) { + const { encoding } = this.options; + const rds = [ + // Important, the windows line ending must be before mac os 9 + Buffer.from("\r\n", encoding), + Buffer.from("\n", encoding), + Buffer.from("\r", encoding) + ]; + loop: for (let i = 0; i < rds.length; i++) { + const l = rds[i].length; + for (let j = 0; j < l; j++) { + if (rds[i][j] !== buf[pos + j]) { + continue loop; + } + } + this.options.record_delimiter.push(rds[i]); + this.state.recordDelimiterMaxLength = rds[i].length; + return rds[i].length; + } + return 0; + }, + __error: function(msg) { + const { encoding, raw, skip_records_with_error } = this.options; + const err = typeof msg === "string" ? new Error(msg) : msg; + if (skip_records_with_error) { + this.state.recordHasError = true; + if (this.options.on_skip !== void 0) { + try { + this.options.on_skip( + err, + raw ? this.state.rawBuffer.toString(encoding) : void 0 + ); + } catch (err2) { + return err2; + } + } + return void 0; + } else { + return err; + } + }, + __infoDataSet: function() { + return { + ...this.info, + columns: this.options.columns + }; + }, + __infoRecord: function() { + const { columns, raw, encoding } = this.options; + return { + ...this.__infoDataSet(), + error: this.state.error, + header: columns === true, + index: this.state.record.length, + raw: raw ? this.state.rawBuffer.toString(encoding) : void 0 + }; + }, + __infoField: function() { + const { columns } = this.options; + const isColumns = Array.isArray(columns); + return { + ...this.__infoRecord(), + column: isColumns === true ? columns.length > this.state.record.length ? columns[this.state.record.length].name : null : this.state.record.length, + quoting: this.state.wasQuoting + }; + } + }; + }; + var parse = function(data, opts = {}) { + if (typeof data === "string") { + data = Buffer.from(data); + } + const records = opts && opts.objname ? {} : []; + const parser = transform(opts); + const push = (record) => { + if (parser.options.objname === void 0) records.push(record); + else { + records[record[0]] = record[1]; + } + }; + const close = () => { + }; + const error = parser.parse(data, true, push, close); + if (error !== void 0) throw error; + return records; + }; + exports.CsvError = CsvError; + exports.parse = parse; + } +}); + +// tools/installer/ide/shared/installed-skills.js +var require_installed_skills = __commonJS({ + "tools/installer/ide/shared/installed-skills.js"(exports, module) { + var path = __require("node:path"); + var fs = require_fs_native(); + var csv = require_sync(); + async function getInstalledCanonicalIds(bmadDir) { + const ids = /* @__PURE__ */ new Set(); + if (!bmadDir) return ids; + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + if (!await fs.pathExists(csvPath)) return ids; + try { + const content = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(content, { columns: true, skip_empty_lines: true }); + for (const record of records) { + if (record.canonicalId) ids.add(record.canonicalId); + } + } catch { + } + return ids; + } + function isBmadOwnedEntry(entry, canonicalIds) { + if (!entry || typeof entry !== "string") return false; + if (entry.toLowerCase().startsWith("bmad-os-")) return false; + if (canonicalIds && canonicalIds.size > 0) return canonicalIds.has(entry); + return entry.toLowerCase().startsWith("bmad"); + } + module.exports = { getInstalledCanonicalIds, isBmadOwnedEntry }; + } +}); + +// src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs +var require_project_root = __commonJS({ + "src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs"(exports, module) { + "use strict"; + var path = __require("node:path"); + function getProjectRoot() { + return process.cwd(); + } + function getSourcePath(...segments) { + return path.join(process.cwd(), ...segments); + } + module.exports = { getProjectRoot, getSourcePath }; + } +}); + +// tools/installer/ide/_config-driven.js +var require_config_driven = __commonJS({ + "tools/installer/ide/_config-driven.js"(exports, module) { + var path = __require("node:path"); + var fs = require_fs_native(); + var yaml = require_dist(); + var prompts = require_prompts(); + var csv = require_sync(); + var { BMAD_FOLDER_NAME } = require_path_utils(); + var { getInstalledCanonicalIds, isBmadOwnedEntry } = require_installed_skills(); + var RESERVED_OPENCODE_COMMANDS = /* @__PURE__ */ new Set([ + "review", + "commit", + "init", + "help", + "skills", + "fast", + "compact", + "clear", + "undo", + "redo", + "edit", + "editor", + "exit", + "quit", + "theme", + "config", + "model", + "session" + ]); + function yamlSafeSingleLine(value) { + const collapsed = String(value).replaceAll(/[\r\n]+/g, " ").trim(); + const needsQuoting = /[:#'"\\]/.test(collapsed) || /^[!&*?|>%@`[{]/.test(collapsed); + if (!needsQuoting) return collapsed; + const escaped = collapsed.replaceAll("\\", "\\\\").replaceAll('"', String.raw`\"`); + return `"${escaped}"`; + } + function isSafeCanonicalId(value) { + return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(value) && !value.includes(".."); + } + var DEFAULT_COMMANDS_BODY_TEMPLATE = "@skills/{canonicalId}"; + async function isAgentSkill(record, bmadDir) { + if (!record?.path || !bmadDir) return false; + const bmadFolderName = path.basename(bmadDir); + const bmadPrefix = bmadFolderName + "/"; + const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; + const tomlPath = path.join(bmadDir, path.dirname(relativePath), "customize.toml"); + if (!await fs.pathExists(tomlPath)) return false; + try { + const content = await fs.readFile(tomlPath, "utf8"); + return /^\[agent\]/m.test(content); + } catch { + return false; + } + } + function expandBodyTemplate(template, { canonicalId, targetDir }) { + return template.replaceAll("{canonicalId}", canonicalId).replaceAll("{target_dir}", targetDir); + } + function buildCommandPointerBody(description, canonicalId, { template, targetDir }) { + const bodyText = expandBodyTemplate(template, { canonicalId, targetDir }); + return `--- +description: ${yamlSafeSingleLine(description)} +--- + +${bodyText} +`; + } + function looksLikeGeneratorOutput(content, canonicalId, { template, targetDir }) { + if (typeof content !== "string") return false; + const trimmed = content.trim(); + const expectedTail = expandBodyTemplate(template, { canonicalId, targetDir }).trim(); + if (!trimmed.endsWith(expectedTail)) return false; + const fmMatch = trimmed.match(/^---\n([\S\s]*?)\n---\n/); + if (!fmMatch) return false; + const fmLines = fmMatch[1].split("\n").filter((l) => l.length > 0); + if (fmLines.length !== 1) return false; + if (!fmLines[0].startsWith("description:")) return false; + return true; + } + var ConfigDrivenIdeSetup = class { + constructor(platformCode, platformConfig) { + this.name = platformCode; + this.displayName = platformConfig.name || platformCode; + this.preferred = platformConfig.preferred || false; + this.platformConfig = platformConfig; + this.installerConfig = platformConfig.installer || null; + this.bmadFolderName = BMAD_FOLDER_NAME; + this.configDir = this.installerConfig?.target_dir || null; + } + setBmadFolderName(bmadFolderName) { + this.bmadFolderName = bmadFolderName; + } + /** + * Detect whether this IDE already has configuration in the project. + * Checks for bmad-prefixed entries in target_dir. + * @param {string} projectDir - Project directory + * @returns {Promise} + */ + async detect(projectDir) { + if (!this.configDir) return false; + const root = projectDir || process.cwd(); + const dir = path.join(root, this.configDir); + if (!await fs.pathExists(dir)) return false; + let entries; + try { + entries = await fs.readdir(dir); + } catch { + return false; + } + const bmadDir = await this._findBmadDir(root); + const canonicalIds = await getInstalledCanonicalIds(bmadDir); + return entries.some((e) => isBmadOwnedEntry(e, canonicalIds)); + } + /** + * Main setup method - called by IdeManager + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + * @returns {Promise} Setup result + */ + async setup(projectDir, bmadDir, options = {}) { + if (this.installerConfig?.ancestor_conflict_check) { + const conflict = await this.findAncestorConflict(projectDir); + if (conflict) { + await prompts.log.error( + `Found existing BMAD skills in ancestor installation: ${conflict} + ${this.name} inherits skills from parent directories, so this would cause duplicates. + Please remove the BMAD files from that directory first: + rm -rf "${conflict}"/bmad*` + ); + return { + success: false, + reason: "ancestor-conflict", + error: `Ancestor conflict: ${conflict}`, + conflictDir: conflict + }; + } + } + if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`); + await this.cleanup(projectDir, options, bmadDir); + if (!this.installerConfig) { + return { success: false, reason: "no-config" }; + } + if (options.skipTarget) { + const results = { skills: 0, sharedTargetHandledByPeer: true }; + if (this.installerConfig.commands_target_dir) { + results.commands = await this.installCommandPointers(projectDir, bmadDir, this.installerConfig, options); + } + return { success: true, results }; + } + if (this.installerConfig.target_dir) { + return this.installToTarget(projectDir, bmadDir, this.installerConfig, options); + } + return { success: false, reason: "invalid-config" }; + } + /** + * Install to a single target directory + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} config - Installation configuration + * @param {Object} options - Setup options + * @returns {Promise} Installation result + */ + async installToTarget(projectDir, bmadDir, config, options) { + const { target_dir } = config; + const targetPath = path.join(projectDir, target_dir); + await fs.ensureDir(targetPath); + this.skillWriteTracker = /* @__PURE__ */ new Set(); + const results = { skills: 0 }; + results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config); + results.skillDirectories = this.skillWriteTracker.size; + if (config.commands_target_dir) { + results.commands = await this.installCommandPointers(projectDir, bmadDir, config, options); + } + await this.printSummary(results, target_dir, options); + this.skillWriteTracker = null; + return { success: true, results }; + } + /** + * Generate per-skill command pointer files for IDEs that surface commands + * separately from skills (e.g. OpenCode's `.opencode/commands/.md`). + * + * Each pointer is a tiny markdown file whose body is `@skills/` + * so invoking `/` routes the user straight to the skill instead + * of forcing them through a `/skills` menu. + * + * Skips: + * - Names that collide with reserved built-in slash commands. + * - canonicalIds that aren't safe basename-only identifiers (defense + * against path traversal even though the manifest is currently trusted). + * - Existing files whose body looks user-modified (preserves hand edits); + * pointer files matching the generator pattern get overwritten so that + * description changes in skill-manifest.csv propagate on re-install. + * + * Per-file write failures are recorded and reported but do not abort the + * rest of the install — pointer files are a non-essential adjunct to the + * skill copy that already succeeded. + * + * @param {string} projectDir + * @param {string} bmadDir + * @param {Object} config - Installer config; reads commands_target_dir. + * @param {Object} options - Setup options. forceCommands overwrites existing + * files unconditionally (including hand-modified ones). + * @returns {Promise} { created, updated, skippedExisting, skippedCollision, skippedInvalidId, writeFailures, fallbackDescription } + */ + async installCommandPointers(projectDir, bmadDir, config, options = {}) { + const result = { + created: 0, + updated: 0, + skippedExisting: 0, + skippedCollision: 0, + skippedInvalidId: 0, + skippedFiltered: 0, + writeFailures: 0, + fallbackDescription: 0 + }; + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + if (!await fs.pathExists(csvPath)) return result; + const commandsPath = path.join(projectDir, config.commands_target_dir); + await fs.ensureDir(commandsPath); + const extension = config.commands_extension || ".md"; + const template = config.commands_body_template || DEFAULT_COMMANDS_BODY_TEMPLATE; + const targetDir = config.target_dir; + const filter = config.commands_filter || null; + const csvContent = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true }); + for (const record of records) { + const canonicalId = record.canonicalId; + if (!canonicalId) continue; + if (!isSafeCanonicalId(canonicalId)) { + result.skippedInvalidId++; + continue; + } + if (filter === "agents-only" && !await isAgentSkill(record, bmadDir)) { + result.skippedFiltered++; + continue; + } + if (this.name === "opencode" && RESERVED_OPENCODE_COMMANDS.has(canonicalId)) { + result.skippedCollision++; + continue; + } + let description = (record.description || "").trim(); + if (!description) { + description = `Run the ${canonicalId} skill`; + result.fallbackDescription++; + } + const body = buildCommandPointerBody(description, canonicalId, { template, targetDir }); + const commandFile = path.join(commandsPath, `${canonicalId}${extension}`); + if (!options.forceCommands && await fs.pathExists(commandFile)) { + let existing; + try { + existing = await fs.readFile(commandFile, "utf8"); + } catch { + result.skippedExisting++; + continue; + } + if (existing === body) { + result.skippedExisting++; + continue; + } + if (looksLikeGeneratorOutput(existing, canonicalId, { template, targetDir })) { + try { + await fs.writeFile(commandFile, body, "utf8"); + result.updated++; + } catch (error) { + result.writeFailures++; + if (!options.silent) { + await prompts.log.warn(`Failed to update command pointer ${canonicalId}${extension}: ${error.message}`); + } + } + continue; + } + result.skippedExisting++; + continue; + } + try { + await fs.writeFile(commandFile, body, "utf8"); + result.created++; + } catch (error) { + result.writeFailures++; + if (!options.silent) { + await prompts.log.warn(`Failed to write command pointer ${canonicalId}${extension}: ${error.message}`); + } + } + } + return result; + } + /** + * Install verbatim native SKILL.md directories from skill-manifest.csv. + * Copies the entire source directory as-is into the IDE skill directory. + * The source SKILL.md is used directly — no frontmatter transformation or file generation. + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {string} targetPath - Target skills directory + * @param {Object} config - Installation configuration + * @returns {Promise} Count of skills installed + */ + async installVerbatimSkills(projectDir, bmadDir, targetPath, config) { + const bmadFolderName = path.basename(bmadDir); + const bmadPrefix = bmadFolderName + "/"; + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + if (!await fs.pathExists(csvPath)) return 0; + const csvContent = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(csvContent, { + columns: true, + skip_empty_lines: true + }); + let count = 0; + for (const record of records) { + const canonicalId = record.canonicalId; + if (!canonicalId) continue; + const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; + const sourceFile = path.join(bmadDir, relativePath); + const sourceDir = path.dirname(sourceFile); + if (!await fs.pathExists(sourceDir)) continue; + const skillDir = path.join(targetPath, canonicalId); + await fs.remove(skillDir); + await fs.ensureDir(skillDir); + this.skillWriteTracker?.add(canonicalId); + const skipPatterns = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", "desktop.ini"]); + const skipSuffixes = ["~", ".swp", ".swo", ".bak"]; + const filter = (src) => { + const name = path.basename(src); + if (src === sourceDir) return true; + if (skipPatterns.has(name)) return false; + if (name.startsWith(".") && name !== ".gitkeep") return false; + if (skipSuffixes.some((s) => name.endsWith(s))) return false; + return true; + }; + await fs.copy(sourceDir, skillDir, { filter }); + count++; + } + return count; + } + /** + * Print installation summary + * @param {Object} results - Installation results + * @param {string} targetDir - Target directory (relative) + */ + async printSummary(results, targetDir, options = {}) { + if (options.silent) return; + const count = results.skillDirectories || results.skills || 0; + if (count > 0) { + await prompts.log.success(`${this.name} configured: ${count} skills → ${targetDir}`); + } + const cmd = results.commands; + if (cmd && (cmd.created > 0 || cmd.updated > 0) && this.installerConfig?.commands_target_dir) { + const total = cmd.created + cmd.updated; + const detail = cmd.updated > 0 ? `${cmd.created} new, ${cmd.updated} refreshed` : `${total}`; + await prompts.log.success(`${this.name} commands: ${detail} → ${this.installerConfig.commands_target_dir}`); + if (cmd.skippedCollision > 0) { + await prompts.log.message(` (${cmd.skippedCollision} skipped — name collides with reserved slash command)`); + } + if (cmd.writeFailures > 0) { + await prompts.log.warn(` (${cmd.writeFailures} pointer writes failed — see warnings above)`); + } + } + } + /** + * Cleanup IDE configuration + * @param {string} projectDir - Project directory + */ + async cleanup(projectDir, options = {}, bmadDir = null) { + const resolvedBmadDir = bmadDir || await this._findBmadDir(projectDir); + let removalSet; + if (options.previousSkillIds) { + removalSet = new Set(options.previousSkillIds); + if (resolvedBmadDir) { + const removals = await this.loadRemovalLists(resolvedBmadDir); + for (const entry of removals) removalSet.add(entry); + } + } else if (resolvedBmadDir) { + removalSet = await this._buildUninstallSet(resolvedBmadDir); + } else { + removalSet = /* @__PURE__ */ new Set(); + } + if (this.name === "github-copilot") { + await this.cleanupCopilotInstructions(projectDir, options); + } + if (this.name === "kilo") { + await this.cleanupKiloModes(projectDir, options); + } + if (this.name === "rovo-dev") { + await this.cleanupRovoDevPrompts(projectDir, options); + } + if (this.installerConfig?.commands_target_dir) { + const isInstallFlow = !!options.previousSkillIds; + const activeSkillIds = isInstallFlow ? await this._readActiveSkillIds(resolvedBmadDir) : /* @__PURE__ */ new Set(); + const extension = this.installerConfig.commands_extension || ".md"; + await this.cleanupCommandPointers( + projectDir, + this.installerConfig.commands_target_dir, + options, + removalSet, + activeSkillIds, + extension + ); + } + if (options.skipTarget) return; + if (this.installerConfig?.target_dir) { + await this.cleanupTarget(projectDir, this.installerConfig.target_dir, options, removalSet); + } + } + /** + * Find the _bmad directory in a project + * @param {string} projectDir - Project directory + * @returns {string|null} Path to bmad dir or null + */ + async _findBmadDir(projectDir) { + const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME); + return await fs.pathExists(bmadDir) ? bmadDir : null; + } + /** + * Build the full set of entries to remove for uninstall. + * Reads skill-manifest.csv to know exactly what was installed, plus removal lists. + * @param {string} bmadDir - BMAD installation directory + * @returns {Set} Set of entries to remove + */ + async _buildUninstallSet(bmadDir) { + const removals = await this.loadRemovalLists(bmadDir); + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + try { + if (await fs.pathExists(csvPath)) { + const content = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(content, { columns: true, skip_empty_lines: true }); + for (const record of records) { + if (record.canonicalId) { + removals.add(record.canonicalId); + } + } + } + } catch { + } + return removals; + } + /** + * Load removal lists from all module sources in the bmad directory. + * Each module can have an optional removals.txt listing entries to remove. + * @param {string} bmadDir - BMAD installation directory + * @returns {Set} Set of entries to remove + */ + async loadRemovalLists(bmadDir) { + const removals = /* @__PURE__ */ new Set(); + const { getProjectRoot } = require_project_root(); + const projectRemovalsPath = path.join(getProjectRoot(), "removals.txt"); + await this._readRemovalFile(projectRemovalsPath, removals); + try { + const entries = await fs.readdir(bmadDir); + for (const entry of entries) { + if (entry.startsWith("_")) continue; + const removalPath = path.join(bmadDir, entry, "removals.txt"); + await this._readRemovalFile(removalPath, removals); + } + } catch { + } + return removals; + } + /** + * Read a removals.txt file and add entries to the set + * @param {string} filePath - Path to removals.txt + * @param {Set} removals - Set to add entries to + */ + async _readRemovalFile(filePath, removals) { + try { + if (await fs.pathExists(filePath)) { + const content = await fs.readFile(filePath, "utf8"); + for (const line of content.split("\n")) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith("#")) { + removals.add(trimmed); + } + } + } + } catch { + } + } + /** + * Cleanup generated command pointer files for entries in removalSet. + * Symmetric counterpart to installCommandPointers — removes + * `` files whose canonicalId is in the set. Removes + * the commands directory entirely if it ends up empty. + * @param {string} projectDir + * @param {string} commandsTargetDir - Relative dir (e.g. .opencode/commands) + * @param {Object} options + * @param {Set} removalSet - canonicalIds whose pointer files to remove + * @param {Set} [activeSkillIds] - canonicalIds present in the + * current manifest. Pointers for IDs in this set are spared so an + * install-flow cleanup (where removalSet === previousSkillIds and the + * same skills are about to be re-installed) doesn't wipe hand-edited + * pointer files. Pass an empty set or omit to delete every match in + * removalSet (uninstall flow). + * @param {string} [extension] - Pointer file extension (default '.md'); + * matches the platform's commands_extension config value so cleanup + * correctly identifies pointer files for IDEs whose convention isn't .md + * (e.g. Copilot's `.agent.md`). + */ + async cleanupCommandPointers(projectDir, commandsTargetDir, options = {}, removalSet = /* @__PURE__ */ new Set(), activeSkillIds = /* @__PURE__ */ new Set(), extension = ".md") { + if (!removalSet || removalSet.size === 0) return; + const commandsPath = path.join(projectDir, commandsTargetDir); + if (!await fs.pathExists(commandsPath)) return; + let entries; + try { + entries = await fs.readdir(commandsPath); + } catch { + return; + } + for (const entry of entries) { + if (!entry.endsWith(extension)) continue; + const canonicalId = entry.slice(0, -extension.length); + if (!removalSet.has(canonicalId)) continue; + if (activeSkillIds.has(canonicalId)) continue; + try { + await fs.remove(path.join(commandsPath, entry)); + } catch { + } + } + try { + const remaining = await fs.readdir(commandsPath); + if (remaining.length === 0) { + await fs.remove(commandsPath); + } + } catch { + } + } + /** + * Read the canonicalIds currently present in the skill-manifest.csv. + * Used by cleanup to distinguish "re-install of an existing skill" + * (preserve pointer) from "skill truly being removed" (delete pointer). + * @param {string|null} bmadDir + * @returns {Promise>} + */ + async _readActiveSkillIds(bmadDir) { + const ids = /* @__PURE__ */ new Set(); + if (!bmadDir) return ids; + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + if (!await fs.pathExists(csvPath)) return ids; + try { + const content = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(content, { columns: true, skip_empty_lines: true }); + for (const record of records) { + if (record.canonicalId) ids.add(record.canonicalId); + } + } catch { + } + return ids; + } + /** + * Cleanup a specific target directory. + * When removalSet is provided, only removes entries in that set. + * When removalSet is null (legacy dirs), removes all bmad-prefixed entries. + * @param {string} projectDir - Project directory + * @param {string} targetDir - Target directory to clean + * @param {Object} options - Cleanup options + * @param {Set|null} removalSet - Entries to remove, or null for legacy prefix matching + */ + async cleanupTarget(projectDir, targetDir, options = {}, removalSet = /* @__PURE__ */ new Set()) { + const targetPath = path.join(projectDir, targetDir); + if (!await fs.pathExists(targetPath)) { + return; + } + if (removalSet && removalSet.size === 0) { + return; + } + let entries; + try { + entries = await fs.readdir(targetPath); + } catch { + return; + } + if (!entries || !Array.isArray(entries)) { + return; + } + let removedCount = 0; + for (const entry of entries) { + if (!entry || typeof entry !== "string") continue; + if (entry.startsWith("bmad-os-")) continue; + const shouldRemove = removalSet ? removalSet.has(entry) : isBmadOwnedEntry(entry, null); + if (shouldRemove) { + try { + await fs.remove(path.join(targetPath, entry)); + removedCount++; + } catch { + } + } + } + if (removedCount > 0) { + try { + const remaining = await fs.readdir(targetPath); + if (remaining.length === 0) { + await fs.remove(targetPath); + } + } catch { + } + } + } + /** + * Strip BMAD-owned content from .github/copilot-instructions.md. + * The old custom installer injected content between and markers. + * Deletes the file if nothing remains. Restores .bak backup if one exists. + */ + async cleanupCopilotInstructions(projectDir, options = {}) { + const filePath = path.join(projectDir, ".github", "copilot-instructions.md"); + if (!await fs.pathExists(filePath)) return; + try { + const content = await fs.readFile(filePath, "utf8"); + const startIdx = content.indexOf(""); + const endIdx = content.indexOf(""); + if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return; + const cleaned = content.slice(0, startIdx) + content.slice(endIdx + "".length); + if (cleaned.trim().length === 0) { + await fs.remove(filePath); + const backupPath = `${filePath}.bak`; + if (await fs.pathExists(backupPath)) { + await fs.rename(backupPath, filePath); + if (!options.silent) await prompts.log.message(" Restored copilot-instructions.md from backup"); + } + } else { + await fs.writeFile(filePath, cleaned, "utf8"); + const backupPath = `${filePath}.bak`; + if (await fs.pathExists(backupPath)) await fs.remove(backupPath); + } + if (!options.silent) await prompts.log.message(" Cleaned BMAD markers from copilot-instructions.md"); + } catch { + if (!options.silent) await prompts.log.warn(" Warning: Could not clean BMAD markers from copilot-instructions.md"); + } + } + /** + * Strip BMAD-owned modes from .kilocodemodes. + * The old custom kilo.js installer added modes with slug starting with 'bmad-'. + * Parses YAML, filters out BMAD modes, rewrites. Leaves file as-is on parse failure. + */ + async cleanupKiloModes(projectDir, options = {}) { + const kiloModesPath = path.join(projectDir, ".kilocodemodes"); + if (!await fs.pathExists(kiloModesPath)) return; + const content = await fs.readFile(kiloModesPath, "utf8"); + let config; + try { + config = yaml.parse(content) || {}; + } catch { + if (!options.silent) await prompts.log.warn(" Warning: Could not parse .kilocodemodes for cleanup"); + return; + } + if (!Array.isArray(config.customModes)) return; + const originalCount = config.customModes.length; + config.customModes = config.customModes.filter((mode) => mode && (!mode.slug || !mode.slug.startsWith("bmad-"))); + const removedCount = originalCount - config.customModes.length; + if (removedCount > 0) { + try { + await fs.writeFile(kiloModesPath, yaml.stringify(config, { lineWidth: 0 })); + if (!options.silent) await prompts.log.message(` Removed ${removedCount} BMAD modes from .kilocodemodes`); + } catch { + if (!options.silent) await prompts.log.warn(" Warning: Could not write .kilocodemodes during cleanup"); + } + } + } + /** + * Strip BMAD-owned entries from .rovodev/prompts.yml. + * The old custom rovodev.js installer registered workflows in prompts.yml. + * Parses YAML, filters out entries with name starting with 'bmad-', rewrites. + * Removes the file if no entries remain. + */ + async cleanupRovoDevPrompts(projectDir, options = {}) { + const promptsPath = path.join(projectDir, ".rovodev", "prompts.yml"); + if (!await fs.pathExists(promptsPath)) return; + const content = await fs.readFile(promptsPath, "utf8"); + let config; + try { + config = yaml.parse(content) || {}; + } catch { + if (!options.silent) await prompts.log.warn(" Warning: Could not parse prompts.yml for cleanup"); + return; + } + if (!Array.isArray(config.prompts)) return; + const originalCount = config.prompts.length; + config.prompts = config.prompts.filter((entry) => entry && (!entry.name || !entry.name.startsWith("bmad-"))); + const removedCount = originalCount - config.prompts.length; + if (removedCount > 0) { + try { + if (config.prompts.length === 0) { + await fs.remove(promptsPath); + } else { + await fs.writeFile(promptsPath, yaml.stringify(config, { lineWidth: 0 })); + } + if (!options.silent) await prompts.log.message(` Removed ${removedCount} BMAD entries from prompts.yml`); + } catch { + if (!options.silent) await prompts.log.warn(" Warning: Could not write prompts.yml during cleanup"); + } + } + } + /** + * Check ancestor directories for existing BMAD files in the same target_dir. + * IDEs like Claude Code inherit commands from parent directories, so an existing + * installation in an ancestor would cause duplicate commands. + * @param {string} projectDir - Project directory being installed to + * @returns {Promise} Path to conflicting directory, or null if clean + */ + async findAncestorConflict(projectDir) { + const targetDir = this.installerConfig?.target_dir; + if (!targetDir) return null; + const resolvedProject = await fs.realpath(path.resolve(projectDir)); + let current = path.dirname(resolvedProject); + const root = path.parse(current).root; + while (current !== root && current.length > root.length) { + const candidatePath = path.join(current, targetDir); + try { + if (await fs.pathExists(candidatePath)) { + const entries = await fs.readdir(candidatePath); + const ancestorBmadDir = await this._findBmadDir(current); + const canonicalIds = await getInstalledCanonicalIds(ancestorBmadDir); + if (entries.some((e) => isBmadOwnedEntry(e, canonicalIds))) { + return candidatePath; + } + } + } catch { + } + current = path.dirname(current); + } + return null; + } + }; + module.exports = { ConfigDrivenIdeSetup }; + } +}); + +// tools/installer/ide/manager.js +var require_manager = __commonJS({ + "tools/installer/ide/manager.js"(exports, module) { + var { BMAD_FOLDER_NAME } = require_path_utils(); + var prompts = require_prompts(); + var IdeManager = class { + constructor() { + this.handlers = /* @__PURE__ */ new Map(); + this._initialized = false; + this.bmadFolderName = BMAD_FOLDER_NAME; + } + /** + * Set the bmad folder name for all IDE handlers + * @param {string} bmadFolderName - The bmad folder name + */ + setBmadFolderName(bmadFolderName) { + this.bmadFolderName = bmadFolderName; + for (const handler of this.handlers.values()) { + if (typeof handler.setBmadFolderName === "function") { + handler.setBmadFolderName(bmadFolderName); + } + } + } + /** + * Ensure handlers are loaded (lazy loading) + */ + async ensureInitialized() { + if (!this._initialized) { + await this.loadHandlers(); + this._initialized = true; + } + } + /** + * Dynamically load all IDE handlers from platform-codes.yaml + */ + async loadHandlers() { + await this.loadConfigDrivenHandlers(); + } + /** + * Load config-driven handlers from platform-codes.yaml + * This creates ConfigDrivenIdeSetup instances for platforms with installer config + */ + async loadConfigDrivenHandlers() { + const { loadPlatformCodes } = require_platform_codes(); + const platformConfig = await loadPlatformCodes(); + const { ConfigDrivenIdeSetup } = require_config_driven(); + for (const [platformCode, platformInfo] of Object.entries(platformConfig.platforms)) { + if (!platformInfo.installer) continue; + const handler = new ConfigDrivenIdeSetup(platformCode, platformInfo); + if (typeof handler.setBmadFolderName === "function") { + handler.setBmadFolderName(this.bmadFolderName); + } + this.handlers.set(platformCode, handler); + } + } + /** + * Get all available IDEs with their metadata + * @returns {Array} Array of IDE information objects + */ + getAvailableIdes() { + const ides = []; + for (const [key, handler] of this.handlers) { + const name = handler.displayName || handler.name || key; + if (!key || !name || typeof key !== "string" || typeof name !== "string") { + continue; + } + if (handler.platformConfig?.suspended) { + continue; + } + ides.push({ + value: key, + name, + preferred: handler.preferred || false + }); + } + ides.sort((a, b) => { + if (a.preferred && !b.preferred) return -1; + if (!a.preferred && b.preferred) return 1; + return a.name.localeCompare(b.name); + }); + return ides; + } + /** + * Get preferred IDEs + * @returns {Array} Array of preferred IDE information + */ + getPreferredIdes() { + return this.getAvailableIdes().filter((ide) => ide.preferred); + } + /** + * Get non-preferred IDEs + * @returns {Array} Array of non-preferred IDE information + */ + getOtherIdes() { + return this.getAvailableIdes().filter((ide) => !ide.preferred); + } + /** + * Setup IDE configuration + * @param {string} ideName - Name of the IDE + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + */ + async setup(ideName, projectDir, bmadDir, options = {}) { + const handler = this.handlers.get(ideName.toLowerCase()); + if (!handler) { + await prompts.log.warn(`IDE '${ideName}' is not yet supported`); + await prompts.log.message(`Supported IDEs: ${[...this.handlers.keys()].join(", ")}`); + return { success: false, ide: ideName, error: "unsupported IDE" }; + } + if (handler.platformConfig?.suspended) { + if (!options.silent) { + await prompts.log.warn(`${handler.displayName || ideName}: ${handler.platformConfig.suspended}`); + } + if (typeof handler.cleanup === "function") { + try { + await handler.cleanup(projectDir, { silent: true }); + } catch { + } + } + return { success: false, ide: ideName, error: "suspended" }; + } + try { + const handlerResult = await handler.setup(projectDir, bmadDir, options); + let detail = ""; + if (handlerResult && handlerResult.results) { + const r = handlerResult.results; + let count = r.skillDirectories || r.skills || 0; + if (count === 0 && r.sharedTargetHandledByPeer && options.sharedSkillCount) { + count = options.sharedSkillCount; + } + const targetDir = handler.installerConfig?.target_dir || null; + if (count > 0 && targetDir) { + detail = `${count} skills → ${targetDir}`; + } else if (count > 0) { + detail = `${count} skills`; + } + } + const success = handlerResult?.success !== false; + return { success, ide: ideName, detail, error: handlerResult?.error, handlerResult }; + } catch (error) { + await prompts.log.error(`Failed to setup ${ideName}: ${error.message}`); + return { success: false, ide: ideName, error: error.message }; + } + } + /** + * Run setup for multiple IDEs as a single batch. + * Dedupes work when several selected platforms share the same target_dir: + * the first platform owns the directory write, peers skip it. + * @param {Array} ideList - IDE names to set up + * @param {string} projectDir + * @param {string} bmadDir + * @param {Object} [options] - Forwarded to each handler.setup + * @returns {Promise} Per-IDE results + */ + async setupBatch(ideList, projectDir, bmadDir, options = {}) { + await this.ensureInitialized(); + const results = []; + const claimedTargets = /* @__PURE__ */ new Map(); + for (const ideName of ideList) { + const handler = this.handlers.get(ideName.toLowerCase()); + if (!handler) { + results.push(await this.setup(ideName, projectDir, bmadDir, options)); + continue; + } + const target = handler.installerConfig?.target_dir || null; + const claim = target ? claimedTargets.get(target) : null; + const skipTarget = !!claim; + const result = await this.setup(ideName, projectDir, bmadDir, { + ...options, + skipTarget, + sharedWith: claim?.firstIde || null, + sharedTarget: target, + sharedSkillCount: claim?.skillCount || 0 + }); + if (target && !claim) { + const writtenCount = result.handlerResult?.results?.skillDirectories || result.handlerResult?.results?.skills || 0; + if (result.success && writtenCount > 0) { + claimedTargets.set(target, { firstIde: ideName, skillCount: writtenCount }); + } + } + results.push(result); + } + return results; + } + /** + * Cleanup IDE configurations + * @param {string} projectDir - Project directory + * @param {Object} [options] - Cleanup options passed through to handlers + */ + async cleanup(projectDir, options = {}) { + const results = []; + for (const [name, handler] of this.handlers) { + try { + await handler.cleanup(projectDir, options); + results.push({ ide: name, success: true }); + } catch (error) { + results.push({ ide: name, success: false, error: error.message }); + } + } + return results; + } + /** + * Cleanup only the IDEs in the provided list + * Falls back to cleanup() (all handlers) if ideList is empty or undefined + * @param {string} projectDir - Project directory + * @param {Array} ideList - List of IDE names to clean up + * @param {Object} [options] - Cleanup options passed through to handlers + * options.remainingIdes - IDE names still installed after this cleanup; used + * to skip target_dir wipe when a co-installed platform shares the dir. + * @returns {Array} Results array + */ + async cleanupByList(projectDir, ideList, options = {}) { + if (!ideList || ideList.length === 0) { + return this.cleanup(projectDir, options); + } + await this.ensureInitialized(); + const results = []; + const lowercaseHandlers = new Map([...this.handlers.entries()].map(([k, v]) => [k.toLowerCase(), v])); + const remainingTargets = /* @__PURE__ */ new Set(); + if (Array.isArray(options.remainingIdes)) { + for (const remaining of options.remainingIdes) { + const h = lowercaseHandlers.get(String(remaining).toLowerCase()); + const t = h?.installerConfig?.target_dir; + if (t) remainingTargets.add(t); + } + } + for (const ideName of ideList) { + const handler = lowercaseHandlers.get(ideName.toLowerCase()); + if (!handler) continue; + const target = handler.installerConfig?.target_dir || null; + const skipTarget = target && remainingTargets.has(target); + const cleanupOptions = skipTarget ? { ...options, skipTarget: true } : options; + try { + await handler.cleanup(projectDir, cleanupOptions); + results.push({ ide: ideName, success: true, skippedTarget: !!skipTarget }); + } catch (error) { + results.push({ ide: ideName, success: false, error: error.message }); + } + } + return results; + } + /** + * Detect installed IDEs + * @param {string} projectDir - Project directory + * @returns {Array} List of detected IDEs + */ + async detectInstalledIdes(projectDir) { + const detected = []; + for (const [name, handler] of this.handlers) { + if (typeof handler.detect === "function" && await handler.detect(projectDir)) { + detected.push(name); + } + } + return detected; + } + }; + module.exports = { IdeManager }; + } +}); + +// tools/installer/core/ide-sync.js +var require_ide_sync = __commonJS({ + "tools/installer/core/ide-sync.js"(exports, module) { + var path = __require("node:path"); + var fs = require_fs_native(); + var { IdeManager } = require_manager(); + var { BMAD_FOLDER_NAME } = require_path_utils(); + var writeOut = (m) => process.stdout.write(`${m} +`); + var writeErr = (m) => process.stderr.write(`${m} +`); + var DEFAULT_LOGGER = { info: writeOut, warn: writeErr, error: writeErr }; + async function syncIdes({ projectRoot, bmadDir, ides, previousSkillIds = [], verbose = false, cleanup = true, silent = false }) { + const validIdes = (ides || []).filter((ide) => ide && typeof ide === "string"); + if (validIdes.length === 0) return { skipped: true, results: [] }; + const ideManager = new IdeManager(); + ideManager.setBmadFolderName(path.basename(bmadDir)); + await ideManager.ensureInitialized(); + const results = await ideManager.setupBatch(validIdes, projectRoot, bmadDir, { + previousSkillIds: new Set(previousSkillIds), + verbose, + silent + }); + if (cleanup) await cleanupBmadSkillDirs(bmadDir); + return { skipped: false, results }; + } + async function cleanupBmadSkillDirs(bmadDir) { + const csv = require_sync(); + const csvPath = path.join(bmadDir, "_config", "skill-manifest.csv"); + if (!await fs.pathExists(csvPath)) return; + const csvContent = await fs.readFile(csvPath, "utf8"); + const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true }); + const bmadFolderName = path.basename(bmadDir); + const bmadPrefix = bmadFolderName + "/"; + for (const record of records) { + if (!record.path) continue; + const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; + const sourceDir = path.dirname(path.join(bmadDir, relativePath)); + if (await fs.pathExists(sourceDir)) { + await fs.remove(sourceDir); + } + } + } + async function readSelectedIdes(bmadDir) { + const yaml = require_dist(); + const manifestPath = path.join(bmadDir, "_config", "manifest.yaml"); + if (!await fs.pathExists(manifestPath)) return []; + try { + const parsed = yaml.parse(await fs.readFile(manifestPath, "utf8")); + return Array.isArray(parsed?.ides) ? parsed.ides.filter((i) => i && typeof i === "string") : []; + } catch { + return []; + } + } + async function runIdeSync(opts = {}) { + const logger = opts.logger || DEFAULT_LOGGER; + const projectRoot = path.resolve(opts.directory || "."); + const bmadDir = path.join(projectRoot, BMAD_FOLDER_NAME); + if (!await fs.pathExists(bmadDir)) { + logger.error(`[ide-sync] no BMAD installation (_bmad/) found in ${projectRoot}. Run \`bmad install\` first.`); + return 2; + } + const ides = await readSelectedIdes(bmadDir); + if (ides.length === 0) { + logger.info("[ide-sync] no IDEs configured in manifest.yaml — nothing to distribute."); + return 0; + } + const previousSkillIds = normalizeIdList(opts.prune); + const { results } = await syncIdes({ + projectRoot, + bmadDir, + ides, + previousSkillIds, + verbose: !!opts.verbose, + // Standalone path prints its own concise [ide-sync] lines; suppress the + // engine's interactive-style status output (errors still surface). + silent: true + }); + let failed = 0; + for (const r of results) { + if (r.success) { + logger.info(`[ide-sync] ${r.ide}: ${r.detail || "configured"}`); + } else { + failed++; + logger.error(`[ide-sync] ${r.ide}: FAILED — ${r.error || "unknown error"}`); + } + } + return failed > 0 ? 1 : 0; + } + function normalizeIdList(value) { + if (!value) return []; + const arr = Array.isArray(value) ? value : String(value).split(","); + return arr.map((s) => String(s).trim()).filter(Boolean); + } + async function runIdeSyncCli2(argv = []) { + const opts = { directory: ".", prune: "", verbose: false }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a.startsWith("--directory=")) { + opts.directory = a.slice("--directory=".length); + continue; + } + if (a.startsWith("--prune=")) { + opts.prune = a.slice("--prune=".length); + continue; + } + switch (a) { + case "-d": + case "--directory": { + opts.directory = argv[++i] ?? "."; + break; + } + case "--prune": { + opts.prune = argv[++i] ?? ""; + break; + } + case "-v": + case "--verbose": { + opts.verbose = true; + break; + } + default: { + break; + } + } + } + return runIdeSync(opts); + } + module.exports = { + syncIdes, + cleanupBmadSkillDirs, + readSelectedIdes, + runIdeSync, + runIdeSyncCli: runIdeSyncCli2 + }; + } +}); + +// tools/installer/ide-sync-entry.mjs +var import_ide_sync = __toESM(require_ide_sync(), 1); +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +var __dir = dirname(fileURLToPath(import.meta.url)); +if (!process.env.BMAD_IDE_PLATFORM_CODES) { + process.env.BMAD_IDE_PLATFORM_CODES = join(__dir, "platform-codes.yaml"); +} +(0, import_ide_sync.runIdeSyncCli)(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => { + process.stderr.write("[ide-sync] " + (err && err.stack || err) + "\n"); + process.exit(1); +}); diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/platform-codes.yaml b/src/core-skills/bmad-module/scripts/lib/vendor/platform-codes.yaml new file mode 100644 index 000000000..b8f18436d --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/vendor/platform-codes.yaml @@ -0,0 +1,322 @@ +# BMAD Platform Codes Configuration +# +# Each platform entry has: +# name: Display name shown to users +# preferred: Whether shown as a recommended option on install +# suspended: (optional) Message explaining why install is blocked +# installer: +# target_dir: Directory where skill directories are installed (project/workspace) +# global_target_dir: (optional) User-home directory for global install +# ancestor_conflict_check: (optional) Refuse install when ancestor dir has BMAD files +# +# Multiple platforms may share the same target_dir or global_target_dir — many tools +# read from the shared `.agents/skills/` and `~/.agents/skills/` cross-tool standard. +# Paths verified against each tool's primary docs as of 2026-04-25. + +platforms: + adal: + name: "AdaL" + preferred: false + installer: + target_dir: .adal/skills + global_target_dir: ~/.adal/skills + + amp: + name: "Sourcegraph Amp" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.config/agents/skills + + antigravity: + name: "Google Antigravity" + preferred: false + installer: + target_dir: .agent/skills + global_target_dir: ~/.gemini/antigravity/skills + + auggie: + name: "Auggie" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + bob: + name: "IBM Bob" + preferred: false + installer: + target_dir: .bob/skills + global_target_dir: ~/.bob/skills + + claude-code: + name: "Claude Code" + preferred: true + installer: + target_dir: .claude/skills + global_target_dir: ~/.claude/skills + + cline: + name: "Cline" + preferred: false + installer: + target_dir: .cline/skills + global_target_dir: ~/.cline/skills + + codex: + name: "Codex" + preferred: true + installer: + target_dir: .agents/skills + global_target_dir: ~/.codex/skills + + codebuddy: + name: "CodeBuddy" + preferred: false + installer: + target_dir: .codebuddy/skills + global_target_dir: ~/.codebuddy/skills + + command-code: + name: "Command Code" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + cortex: + name: "Snowflake Cortex Code" + preferred: false + installer: + target_dir: .cortex/skills + global_target_dir: ~/.snowflake/cortex/skills + + crush: + name: "Crush" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.config/agents/skills + + cursor: + name: "Cursor" + preferred: true + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + droid: + name: "Factory Droid" + preferred: false + installer: + target_dir: .factory/skills + global_target_dir: ~/.factory/skills + + firebender: + name: "Firebender" + preferred: false + installer: + target_dir: .firebender/skills + global_target_dir: ~/.agents/skills + + gemini: + name: "Gemini CLI" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + github-copilot: + name: "GitHub Copilot" + preferred: true + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + commands_target_dir: .github/agents + commands_extension: .agent.md + commands_body_template: "LOAD the FULL {project-root}/{target_dir}/{canonicalId}/SKILL.md, READ its entire contents and follow its directions exactly!" + # The Custom Agents picker should only show persona agents (not + # workflows/tools). Detected by reading each skill's source + # `customize.toml` and checking for an `[agent]` section — that's + # the actual configuration source of truth: every BMAD persona is + # configured under `[agent]`, every workflow under `[workflow]`, + # every standalone skill has no customize.toml. This signal is + # naming-independent, so personas like `bmad-tea` (which doesn't + # follow the `-agent-` convention) are still included, and + # meta-skills like `bmad-agent-builder` (which contains `-agent-` + # but is a skill-builder workflow, not a persona) are correctly + # excluded. + commands_filter: agents-only + + goose: + name: "Block Goose" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.config/agents/skills + + iflow: + name: "iFlow" + preferred: false + installer: + target_dir: .iflow/skills + global_target_dir: ~/.iflow/skills + + junie: + name: "Junie" + preferred: false + installer: + target_dir: .junie/skills + global_target_dir: ~/.junie/skills + + kilo: + name: "KiloCoder" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.kilocode/skills + + kimi-code: + name: "Kimi Code" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + kiro: + name: "Kiro" + preferred: false + installer: + target_dir: .kiro/skills + global_target_dir: ~/.kiro/skills + + kode: + name: "Kode" + preferred: false + installer: + target_dir: .kode/skills + global_target_dir: ~/.kode/skills + + mistral-vibe: + name: "Mistral Vibe" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.vibe/skills + + mux: + name: "Mux" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + neovate: + name: "Neovate" + preferred: false + installer: + target_dir: .neovate/skills + global_target_dir: ~/.neovate/skills + + ona: + name: "Ona" + preferred: false + installer: + target_dir: .ona/skills + + openclaw: + name: "OpenClaw" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + opencode: + name: "OpenCode" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + commands_target_dir: .opencode/commands + + openhands: + name: "OpenHands" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + pi: + name: "Pi" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + pochi: + name: "Pochi" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + qoder: + name: "Qoder" + preferred: false + installer: + target_dir: .qoder/skills + global_target_dir: ~/.qoder/skills + + qwen: + name: "QwenCoder" + preferred: false + installer: + target_dir: .qwen/skills + global_target_dir: ~/.qwen/skills + + replit: + name: "Replit Agent" + preferred: false + installer: + target_dir: .agents/skills + + roo: + name: "Roo Code" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + rovo-dev: + name: "Rovo Dev" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + trae: + name: "Trae" + preferred: false + installer: + target_dir: .trae/skills + + warp: + name: "Warp" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + windsurf: + name: "Windsurf" + preferred: false + installer: + target_dir: .agents/skills + global_target_dir: ~/.agents/skills + + zencoder: + name: "Zencoder" + preferred: false + installer: + target_dir: .zencoder/skills + global_target_dir: ~/.zencoder/skills diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs b/src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs new file mode 100644 index 000000000..bf894c8f8 --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/vendor/shims/project-root.cjs @@ -0,0 +1,18 @@ +// Build-time shim for tools/installer/project-root.js, injected into the +// ide-sync bundle. The full module reaches into custom-module-manager and the +// rest of the installer; the IDE engine only needs getProjectRoot() (to locate +// an optional project-level removals.txt). In the bundle, the project root is +// the cwd the bmad-module skill runs the bundle from. +'use strict'; + +const path = require('node:path'); + +function getProjectRoot() { + return process.cwd(); +} + +function getSourcePath(...segments) { + return path.join(process.cwd(), ...segments); +} + +module.exports = { getProjectRoot, getSourcePath }; diff --git a/src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs b/src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs new file mode 100644 index 000000000..07d08f5ad --- /dev/null +++ b/src/core-skills/bmad-module/scripts/lib/vendor/shims/prompts.cjs @@ -0,0 +1,53 @@ +// Build-time shim for tools/installer/prompts.js, injected into the ide-sync +// bundle so the heavyweight interactive @clack/prompts dependency is never +// pulled in. The IDE engine only uses `prompts.log.*` for status output; that +// maps to plain stdout/stderr here. Interactive helpers throw if reached (they +// must not be during non-interactive distribution). +'use strict'; + +const out = (m) => process.stdout.write(`${m}\n`); +const err = (m) => process.stderr.write(`${m}\n`); + +const log = { + info: async (m) => out(m), + success: async (m) => out(m), + message: async (m) => out(m), + step: async (m) => out(m), + warn: async (m) => err(m), + error: async (m) => err(m), +}; + +const notInteractive = () => { + throw new Error('interactive prompt is not available in the ide-sync bundle'); +}; + +// Identity color helper: every method returns its input unchanged. +const identityColor = new Proxy( + {}, + { + get: () => (s) => s, + }, +); + +module.exports = { + log, + getColor: async () => identityColor, + spinner: () => ({ start() {}, stop() {}, message() {} }), + tasks: async () => {}, + note: async (m) => out(m), + box: async (m) => out(m), + intro: async () => {}, + outro: async () => {}, + cancel: async () => {}, + handleCancel: async () => {}, + getClack: notInteractive, + select: notInteractive, + multiselect: notInteractive, + autocomplete: notInteractive, + autocompleteMultiselect: notInteractive, + directory: notInteractive, + confirm: notInteractive, + text: notInteractive, + password: notInteractive, + prompt: notInteractive, +}; diff --git a/src/core-skills/bmad-module/scripts/remove.mjs b/src/core-skills/bmad-module/scripts/remove.mjs index e03bdb558..787173daa 100644 --- a/src/core-skills/bmad-module/scripts/remove.mjs +++ b/src/core-skills/bmad-module/scripts/remove.mjs @@ -9,7 +9,9 @@ import { removeSkillManifestRows, removeFilesManifestRows, readFileEntriesForModule, + readSkillCanonicalIdsForModule, } from './lib/manifest-ops.mjs'; +import { distributeToIdes } from './lib/ide-sync.mjs'; // Remove a module's installed files and manifest entries. With `--purge` also // deletes `_bmad/custom//` (user customization dir). Without it, customs @@ -37,6 +39,10 @@ export async function runRemove(opts) { ); } + // Capture the module's distributed skill ids before dropping its manifest + // rows, so we can prune them from the IDE directories afterward. + const removedSkillIds = await readSkillCanonicalIdsForModule(bmadDir, code); + // Delete each file tracked in files-manifest.csv; prune empty dirs after. const fileEntries = await readFileEntriesForModule(bmadDir, code); const moduleRoot = path.join(bmadDir, code); @@ -65,6 +71,14 @@ export async function runRemove(opts) { await removeSkillManifestRows(bmadDir, code); await removeModuleFromManifest(bmadDir, code); + // Prune the module's skills from every configured coding assistant. The + // manifest no longer lists the module, so ide-sync removes its skill dirs + + // command pointers and re-syncs the rest. + const ideResult = await distributeToIdes({ projectDir, bmadDir, prune: removedSkillIds }); + if (!ideResult.skipped && !ideResult.ok) { + process.stderr.write(`[bmad-module] warning: ${ideResult.hint}\n`); + } + process.stdout.write(`[bmad-module] removed ${code} (${fileEntries.length} file(s))\n`); if (opts.purge) { process.stdout.write(`[bmad-module] purged _bmad/custom/${code}/\n`); diff --git a/src/core-skills/bmad-module/scripts/update.mjs b/src/core-skills/bmad-module/scripts/update.mjs index d343767a0..ffe634356 100644 --- a/src/core-skills/bmad-module/scripts/update.mjs +++ b/src/core-skills/bmad-module/scripts/update.mjs @@ -13,7 +13,9 @@ import { removeSkillManifestRows, removeFilesManifestRows, readFileEntriesForModule, + readSkillCanonicalIdsForModule, } from './lib/manifest-ops.mjs'; +import { distributeToIdes } from './lib/ide-sync.mjs'; // Update one installed module (or all when opts.all is true). v1 semantics: // - Re-resolves the original source (or new --ref) and re-clones. @@ -68,6 +70,11 @@ async function updateOne(bmadDir, projectDir, entry, opts) { ); } + // Capture the currently-distributed skill ids before we rewrite the + // manifest, so any skill dropped between versions is pruned from the IDE + // directories (and re-distributed ones are refreshed). + const oldSkillIds = await readSkillCanonicalIdsForModule(bmadDir, code); + // Modified-file check: any tracked file whose on-disk hash diverges from // the recorded one is treated as user-modified. Abort rather than clobber. const oldEntries = await readFileEntriesForModule(bmadDir, code); @@ -130,6 +137,13 @@ async function updateOne(bmadDir, projectDir, entry, opts) { `[bmad-module] updated ${code} (${manifest.name} ${manifest.version})${materialized.sha ? ` @ ${materialized.sha.slice(0, 7)}` : ''}\n`, ); process.stdout.write(`[bmad-module] previous ${oldEntries.length} file(s) → new ${destPaths.length} file(s)\n`); + + // Re-distribute to the configured coding assistants: prune skills that no + // longer exist in this version, refresh the rest. + const ideResult = await distributeToIdes({ projectDir, bmadDir, prune: oldSkillIds }); + if (!ideResult.skipped && !ideResult.ok) { + process.stderr.write(`[bmad-module] warning: ${ideResult.hint}\n`); + } } finally { await materialized.cleanup(); } diff --git a/src/core-skills/bmad-module/tests/integration.test.sh b/src/core-skills/bmad-module/tests/integration.test.sh index 8f677861e..7ca2cc611 100755 --- a/src/core-skills/bmad-module/tests/integration.test.sh +++ b/src/core-skills/bmad-module/tests/integration.test.sh @@ -207,6 +207,44 @@ note "remove unknown code" run remove nope assert_exit 90 "remove unknown" +# ─── 13. IDE distribution into the user's chosen coding assistants ─────────── +# Uses a SEPARATE project whose manifest lists two IDEs, so install/remove must +# push skills to (and prune them from) those IDE dirs via the vendored ide-sync +# bundle. Fully offline — no npx, no network, no node_modules. +note "IDE distribution: install/remove sync to configured assistants" +IDEPROJ="${WORKDIR}/ideproj" +mkdir -p "${IDEPROJ}/_bmad/_config" +cat > "${IDEPROJ}/_bmad/_config/manifest.yaml" <<'YAML' +installation: + version: "v6.7.1" + installDate: "2026-05-21T00:00:00.000Z" + lastUpdated: "2026-05-21T00:00:00.000Z" +modules: [] +ides: + - claude-code + - cursor +YAML +printf 'canonicalId,name,description,module,path\n' > "${IDEPROJ}/_bmad/_config/skill-manifest.csv" +printf 'type,name,module,path,hash\n' > "${IDEPROJ}/_bmad/_config/files-manifest.csv" + +run install "${EXAMPLES}/minimal/acme-md-lint" --project-dir "${IDEPROJ}" +assert_exit 0 "install into IDE project" +assert_path_exists "${IDEPROJ}/.claude/skills/acme-md-lint/SKILL.md" +assert_path_exists "${IDEPROJ}/.agents/skills/acme-md-lint/SKILL.md" +[[ "${STDOUT}" == *"claude-code"* ]] && ok "stdout reports claude-code distribution" \ + || ko "expected claude-code in stdout: ${STDOUT}" +# Canonical end-state: skill source dirs removed from _bmad/ after distribution. +if find "${IDEPROJ}/_bmad" -name SKILL.md | grep -q .; then + ko "SKILL.md still under _bmad after distribution" +else + ok "_bmad skill dirs cleaned after distribution" +fi + +run remove mdlint --project-dir "${IDEPROJ}" +assert_exit 0 "remove from IDE project" +assert_path_absent "${IDEPROJ}/.claude/skills/acme-md-lint" +assert_path_absent "${IDEPROJ}/.agents/skills/acme-md-lint" + # ─── Summary ───────────────────────────────────────────────────────────────── echo echo "──────────────────────────────────────────────────────────────────────" diff --git a/test/test-ide-sync.js b/test/test-ide-sync.js new file mode 100644 index 000000000..2f9584652 --- /dev/null +++ b/test/test-ide-sync.js @@ -0,0 +1,123 @@ +// test-ide-sync — behavioral drift guard for the IDE-distribution path. +// +// The bmad-module skill runs a self-contained esbuild bundle +// (src/core-skills/bmad-module/scripts/lib/vendor/ide-sync.mjs) built FROM the +// real engine (tools/installer/ide/* via core/ide-sync.js). vendor:check already +// byte-verifies the bundle matches its source. This test verifies the two +// delivery vehicles behave IDENTICALLY at runtime: +// 1. `bmad ide-sync` — the engine, run directly from the package +// 2. `vendor/ide-sync.mjs` — the shipped, dependency-free bundle +// Both must produce the same IDE skill trees for the same project, including +// `--prune`. If the engine changes without rebuilding the bundle, the outputs +// diverge and this fails (complementing the byte-level vendor:check). + +const assert = require('node:assert'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); +const { spawnSync } = require('node:child_process'); + +const repoRoot = path.resolve(__dirname, '..'); +const CLI = path.join(repoRoot, 'tools', 'installer', 'bmad-cli.js'); +const BUNDLE = path.join(repoRoot, 'src', 'core-skills', 'bmad-module', 'scripts', 'lib', 'vendor', 'ide-sync.mjs'); + +let passed = 0; +let failed = 0; +function check(label, fn) { + try { + fn(); + passed++; + process.stdout.write(` ✓ ${label}\n`); + } catch (error) { + failed++; + process.stdout.write(` ✗ ${label}\n ${error.message}\n`); + } +} + +// Build a fresh project with two skills recorded for two IDEs. +function makeProject(skillIds) { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bmad-ide-sync-')); + fs.mkdirSync(path.join(dir, '_bmad', '_config'), { recursive: true }); + fs.writeFileSync( + path.join(dir, '_bmad', '_config', 'manifest.yaml'), + 'installation:\n version: "0.0.0"\nmodules:\n - name: demo\n source: community\nides:\n - claude-code\n - cursor\n', + ); + let csv = 'canonicalId,name,description,module,path\n'; + for (const id of skillIds) { + const sd = path.join(dir, '_bmad', 'demo', 'skills', id); + fs.mkdirSync(sd, { recursive: true }); + fs.writeFileSync(path.join(sd, 'SKILL.md'), `---\nname: ${id}\ndescription: ${id} demo\n---\nbody ${id}\n`); + csv += `"${id}","${id}","${id} demo","demo","_bmad/demo/skills/${id}/SKILL.md"\n`; + } + fs.writeFileSync(path.join(dir, '_bmad', '_config', 'skill-manifest.csv'), csv); + return dir; +} + +// Snapshot the IDE skill trees (relative path -> file contents) for comparison. +function snapshotIdeDirs(projectDir) { + const snap = {}; + for (const rel of ['.claude/skills', '.agents/skills']) { + const base = path.join(projectDir, rel); + if (!fs.existsSync(base)) continue; + const walk = (d) => { + for (const entry of fs.readdirSync(d, { withFileTypes: true })) { + const full = path.join(d, entry.name); + if (entry.isDirectory()) walk(full); + else snap[path.relative(projectDir, full)] = fs.readFileSync(full, 'utf8'); + } + }; + walk(base); + } + return snap; +} + +function runEngine(projectDir, prune) { + const args = [CLI, 'ide-sync', '-d', projectDir]; + if (prune) args.push('--prune', prune); + const r = spawnSync(process.execPath, args, { encoding: 'utf8' }); + assert.strictEqual(r.status, 0, `engine ide-sync exited ${r.status}: ${r.stderr}`); +} + +function runBundle(projectDir, prune) { + const args = [BUNDLE, '-d', projectDir]; + if (prune) args.push('--prune', prune); + const r = spawnSync(process.execPath, args, { encoding: 'utf8' }); + assert.strictEqual(r.status, 0, `bundle ide-sync exited ${r.status}: ${r.stderr}`); +} + +process.stdout.write('IDE-sync engine/bundle parity\n'); + +check('bundle exists (run `npm run vendor:build` if missing)', () => { + assert.ok(fs.existsSync(BUNDLE), `missing ${BUNDLE}`); +}); + +const cleanup = []; +try { + // Distribute: engine vs bundle must yield identical IDE trees. + check('distribute: engine == bundle', () => { + const a = makeProject(['sk-a', 'sk-b']); + const b = makeProject(['sk-a', 'sk-b']); + cleanup.push(a, b); + runEngine(a); + runBundle(b); + assert.deepStrictEqual(snapshotIdeDirs(a), snapshotIdeDirs(b)); + assert.ok(fs.existsSync(path.join(a, '.claude', 'skills', 'sk-a', 'SKILL.md')), 'engine did not distribute'); + }); + + // Prune one skill (the remove path): engine vs bundle must agree. + check('prune: engine == bundle and removes pruned skill', () => { + const a = makeProject(['sk-a']); // sk-b dropped from manifest + const b = makeProject(['sk-a']); + cleanup.push(a, b); + runEngine(a, 'sk-b'); + runBundle(b, 'sk-b'); + assert.deepStrictEqual(snapshotIdeDirs(a), snapshotIdeDirs(b)); + assert.ok(!fs.existsSync(path.join(a, '.claude', 'skills', 'sk-b')), 'pruned skill should be gone'); + assert.ok(fs.existsSync(path.join(a, '.claude', 'skills', 'sk-a')), 'kept skill should remain'); + }); +} finally { + for (const d of cleanup) fs.rmSync(d, { recursive: true, force: true }); +} + +process.stdout.write(`\n ${passed} pass · ${failed} fail\n`); +process.exit(failed > 0 ? 1 : 0); diff --git a/tools/installer/commands/ide-sync.js b/tools/installer/commands/ide-sync.js new file mode 100644 index 000000000..4eb532e8f --- /dev/null +++ b/tools/installer/commands/ide-sync.js @@ -0,0 +1,34 @@ +const { runIdeSync } = require('../core/ide-sync'); + +// `bmad ide-sync` — distribute the skills recorded in _config/skill-manifest.csv +// to every coding assistant listed under `ides:` in _config/manifest.yaml, then +// reach the canonical end-state (skills in IDE dirs, removed from _bmad/). +// +// Non-interactive by design: it reads the existing manifest rather than +// prompting, so it is safe to run from scripts and without a TTY. It is the same +// distribution the full `bmad install` performs (both route through +// core/ide-sync.js → IdeManager.setupBatch), exposed as a standalone step. The +// bmad-module skill invokes the bundled equivalent after install/update/remove. +module.exports = { + command: 'ide-sync', + description: "Sync installed skills to the coding assistants configured in this project's manifest", + options: [ + ['-d, --directory ', 'Project directory containing _bmad/', '.'], + ['--prune ', 'Comma-separated canonicalIds to remove from IDE directories'], + ['-v, --verbose', 'Verbose output'], + ], + action: async (options) => { + try { + const code = await runIdeSync({ + directory: options.directory || '.', + prune: options.prune || '', + verbose: !!options.verbose, + }); + process.exit(code); + } catch (error) { + process.stderr.write(`[ide-sync] failed: ${error.message}\n`); + if (process.env.BMAD_DEBUG) process.stderr.write(`${error.stack}\n`); + process.exit(1); + } + }, +}; diff --git a/tools/installer/core/ide-sync.js b/tools/installer/core/ide-sync.js new file mode 100644 index 000000000..ac3acc636 --- /dev/null +++ b/tools/installer/core/ide-sync.js @@ -0,0 +1,215 @@ +// ide-sync — the single, non-interactive primitive for distributing installed +// BMAD skills to the coding assistants (IDEs) recorded in a project's manifest. +// +// This is the ONE implementation of "push skills to the chosen IDEs". Three +// callers route through it so they can never diverge: +// 1. The interactive installer (`Installer._setupIdes` → syncIdes). +// 2. The `bmad ide-sync` CLI command (commands/ide-sync.js → runIdeSync). +// 3. The self-contained bundle shipped into projects at install time and +// invoked by the bmad-module skill (build target wraps runIdeSyncCli). +// +// It reuses the real config-driven IDE engine (IdeManager / ConfigDrivenIdeSetup +// / platform-codes.yaml), so new platforms and handler changes flow here for +// free. The engine is bundleable (fs-native is zero-dep; yaml/csv-parse inline; +// `../prompts` and `../project-root` are aliased to small shims at bundle time). + +const path = require('node:path'); +const fs = require('../fs-native'); +const { IdeManager } = require('../ide/manager'); +const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils'); + +const writeOut = (m) => process.stdout.write(`${m}\n`); +const writeErr = (m) => process.stderr.write(`${m}\n`); +const DEFAULT_LOGGER = { info: writeOut, warn: writeErr, error: writeErr }; + +/** + * Distribute the skills currently listed in _config/skill-manifest.csv to each + * selected IDE, prune any `previousSkillIds` no longer present, then remove the + * now-redundant skill source dirs from _bmad/ (canonical end-state: skills live + * in IDE dirs). + * + * @param {Object} args + * @param {string} args.projectRoot Project root (contains _bmad/). + * @param {string} args.bmadDir Path to the _bmad/ directory. + * @param {string[]} args.ides Platform codes to set up (from manifest.yaml `ides`). + * @param {string[]} [args.previousSkillIds] canonicalIds to remove from IDE dirs. + * @param {boolean} [args.verbose] + * @param {boolean} [args.cleanup] Remove _bmad/ skill source dirs afterward (default true). + * The interactive installer passes false and runs its own + * unconditional cleanup step. + * @returns {Promise<{skipped: boolean, results: Array}>} + */ +async function syncIdes({ projectRoot, bmadDir, ides, previousSkillIds = [], verbose = false, cleanup = true, silent = false }) { + const validIdes = (ides || []).filter((ide) => ide && typeof ide === 'string'); + if (validIdes.length === 0) return { skipped: true, results: [] }; + + const ideManager = new IdeManager(); + ideManager.setBmadFolderName(path.basename(bmadDir)); + await ideManager.ensureInitialized(); + + const results = await ideManager.setupBatch(validIdes, projectRoot, bmadDir, { + previousSkillIds: new Set(previousSkillIds), + verbose, + silent, + }); + + // Mirror Installer._cleanupSkillDirs: skills are self-contained in IDE dirs, + // so _bmad/ only needs module-level files. + if (cleanup) await cleanupBmadSkillDirs(bmadDir); + + return { skipped: false, results }; +} + +/** + * Remove skill source directories from _bmad/ after IDE distribution. Reads + * _config/skill-manifest.csv and removes the parent dir of each listed SKILL.md + * (skipping any already gone). Non-skill module files are left untouched. + * Shared with Installer._cleanupSkillDirs so there is one implementation. + * @param {string} bmadDir + */ +async function cleanupBmadSkillDirs(bmadDir) { + const csv = require('csv-parse/sync'); + const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); + if (!(await fs.pathExists(csvPath))) return; + + const csvContent = await fs.readFile(csvPath, 'utf8'); + const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true }); + const bmadFolderName = path.basename(bmadDir); + const bmadPrefix = bmadFolderName + '/'; + + for (const record of records) { + if (!record.path) continue; + const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; + const sourceDir = path.dirname(path.join(bmadDir, relativePath)); + if (await fs.pathExists(sourceDir)) { + await fs.remove(sourceDir); + } + } +} + +/** + * Read the selected IDE platform codes from _config/manifest.yaml. + * @param {string} bmadDir + * @returns {Promise} + */ +async function readSelectedIdes(bmadDir) { + const yaml = require('yaml'); + const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml'); + if (!(await fs.pathExists(manifestPath))) return []; + try { + const parsed = yaml.parse(await fs.readFile(manifestPath, 'utf8')); + return Array.isArray(parsed?.ides) ? parsed.ides.filter((i) => i && typeof i === 'string') : []; + } catch { + return []; + } +} + +/** + * End-to-end run used by the CLI command and the shipped bundle: resolve paths, + * read the chosen IDEs from the manifest, distribute, and report. Returns a + * process exit code (0 ok, 1 failure, 2 no install). + * + * @param {Object} opts + * @param {string} [opts.directory] Project dir (default '.'). + * @param {string|string[]} [opts.prune] canonicalIds to remove (CSV string or array). + * @param {boolean} [opts.verbose] + * @param {Object} [opts.logger] { info, warn, error } + * @returns {Promise} exit code + */ +async function runIdeSync(opts = {}) { + const logger = opts.logger || DEFAULT_LOGGER; + const projectRoot = path.resolve(opts.directory || '.'); + const bmadDir = path.join(projectRoot, BMAD_FOLDER_NAME); + + if (!(await fs.pathExists(bmadDir))) { + logger.error(`[ide-sync] no BMAD installation (_bmad/) found in ${projectRoot}. Run \`bmad install\` first.`); + return 2; + } + + const ides = await readSelectedIdes(bmadDir); + if (ides.length === 0) { + logger.info('[ide-sync] no IDEs configured in manifest.yaml — nothing to distribute.'); + return 0; + } + + const previousSkillIds = normalizeIdList(opts.prune); + + const { results } = await syncIdes({ + projectRoot, + bmadDir, + ides, + previousSkillIds, + verbose: !!opts.verbose, + // Standalone path prints its own concise [ide-sync] lines; suppress the + // engine's interactive-style status output (errors still surface). + silent: true, + }); + + let failed = 0; + for (const r of results) { + if (r.success) { + logger.info(`[ide-sync] ${r.ide}: ${r.detail || 'configured'}`); + } else { + failed++; + logger.error(`[ide-sync] ${r.ide}: FAILED — ${r.error || 'unknown error'}`); + } + } + return failed > 0 ? 1 : 0; +} + +/** Parse a comma-separated string or array of canonicalIds into a clean array. */ +function normalizeIdList(value) { + if (!value) return []; + const arr = Array.isArray(value) ? value : String(value).split(','); + return arr.map((s) => String(s).trim()).filter(Boolean); +} + +/** + * argv entry point for the shipped bundle. Parses a tiny flag set and calls + * runIdeSync. Intentionally dependency-free (no commander) so the bundle stays + * small and self-contained. + * @param {string[]} argv process.argv.slice(2) + * @returns {Promise} exit code + */ +async function runIdeSyncCli(argv = []) { + const opts = { directory: '.', prune: '', verbose: false }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a.startsWith('--directory=')) { + opts.directory = a.slice('--directory='.length); + continue; + } + if (a.startsWith('--prune=')) { + opts.prune = a.slice('--prune='.length); + continue; + } + switch (a) { + case '-d': + case '--directory': { + opts.directory = argv[++i] ?? '.'; + break; + } + case '--prune': { + opts.prune = argv[++i] ?? ''; + break; + } + case '-v': + case '--verbose': { + opts.verbose = true; + break; + } + default: { + break; + } + } + } + return runIdeSync(opts); +} + +module.exports = { + syncIdes, + cleanupBmadSkillDirs, + readSelectedIdes, + runIdeSync, + runIdeSyncCli, +}; diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index 9347e1f0b..afbc440a7 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -372,21 +372,27 @@ class Installer { async _setupIdes(config, allModules, paths, addResult, previousSkillIds = new Set()) { if (config.skipIde || !config.ides || config.ides.length === 0) return; - await this.ideManager.ensureInitialized(); const validIdes = config.ides.filter((ide) => ide && typeof ide === 'string'); - if (validIdes.length === 0) { addResult('IDE configuration', 'warn', 'no valid IDEs selected'); return; } - const setupResults = await this.ideManager.setupBatch(validIdes, paths.projectRoot, paths.bmadDir, { - selectedModules: allModules || [], + // Route through the shared distribution primitive so the interactive + // installer and the standalone `bmad ide-sync` command can never diverge. + // cleanup:false — the install flow runs its own unconditional + // _cleanupSkillDirs afterward (it must run even when no IDEs are selected). + const { syncIdes } = require('./ide-sync'); + const { results } = await syncIdes({ + projectRoot: paths.projectRoot, + bmadDir: paths.bmadDir, + ides: validIdes, + previousSkillIds: [...previousSkillIds], verbose: config.verbose, - previousSkillIds, + cleanup: false, }); - for (const setupResult of setupResults) { + for (const setupResult of results) { const ide = setupResult.ide; if (setupResult.success) { addResult(ide, 'ok', setupResult.detail || ''); @@ -401,26 +407,12 @@ class Installer { * Skills are self-contained in IDE directories, so _bmad/ only needs * module-level files (config.yaml, _config/, etc.). * Also cleans up skill dirs left by older installer versions. + * Delegates to the shared implementation so there is one copy of this logic. * @param {string} bmadDir - BMAD installation directory */ async _cleanupSkillDirs(bmadDir) { - const csv = require('csv-parse/sync'); - const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); - if (!(await fs.pathExists(csvPath))) return; - - const csvContent = await fs.readFile(csvPath, 'utf8'); - const records = csv.parse(csvContent, { columns: true, skip_empty_lines: true }); - const bmadFolderName = path.basename(bmadDir); - const bmadPrefix = bmadFolderName + '/'; - - for (const record of records) { - if (!record.path) continue; - const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path; - const sourceDir = path.dirname(path.join(bmadDir, relativePath)); - if (await fs.pathExists(sourceDir)) { - await fs.remove(sourceDir); - } - } + const { cleanupBmadSkillDirs } = require('./ide-sync'); + await cleanupBmadSkillDirs(bmadDir); } async _readSkillManifestRows(bmadDir) { diff --git a/tools/installer/ide/platform-codes.js b/tools/installer/ide/platform-codes.js index 6d1aa9180..865b89955 100644 --- a/tools/installer/ide/platform-codes.js +++ b/tools/installer/ide/platform-codes.js @@ -2,10 +2,20 @@ const fs = require('../fs-native'); const path = require('node:path'); const yaml = require('yaml'); -const PLATFORM_CODES_PATH = path.join(__dirname, 'platform-codes.yaml'); - let _cachedPlatformCodes = null; +/** + * Resolve the platform-codes.yaml path. Defaults to the copy beside this file, + * but honors BMAD_IDE_PLATFORM_CODES so the self-contained bundle the + * bmad-module skill ships can point at the YAML beside it (esbuild output does + * not preserve this file's original __dirname). Resolved lazily so the env var + * can be set before the first load. + * @returns {string} + */ +function resolvePlatformCodesPath() { + return process.env.BMAD_IDE_PLATFORM_CODES || path.join(__dirname, 'platform-codes.yaml'); +} + /** * Load the platform codes configuration from YAML * @returns {Object} Platform codes configuration @@ -15,6 +25,7 @@ async function loadPlatformCodes() { return _cachedPlatformCodes; } + const PLATFORM_CODES_PATH = resolvePlatformCodesPath(); if (!(await fs.pathExists(PLATFORM_CODES_PATH))) { throw new Error(`Platform codes configuration not found at: ${PLATFORM_CODES_PATH}`); }