@ebay-dt/bonfire-cli 0.1.0-alpha.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/command.d.ts +5 -1
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +103 -1
- package/dist/command.js.map +1 -1
- package/dist/convert.d.ts +47 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +509 -0
- package/dist/convert.js.map +1 -0
- package/package.json +5 -4
- package/templates/instance/context/PROTOTYPE.md +108 -0
- package/templates/instance/context/ROOT.md +23 -0
- package/templates/instance/context/hooks/block-env-reads.mjs +144 -0
- package/templates/instance/context/mcp.json +8 -0
- package/templates/instance/context/settings.json +19 -0
- package/templates/instance/context/skills/color-scheme/SKILL.md +98 -0
- package/templates/instance/context/skills/item-data/SKILL.md +145 -0
- package/templates/instance/context/skills/setup-evomcp/SKILL.md +73 -0
- package/templates/instance/next.config.ts +10 -0
- package/templates/instance/src/actions/agents.ts +15 -0
- package/templates/instance/src/actions/prototypes.ts +37 -0
- package/templates/instance/src/app/(prototypes)/layout.tsx +40 -0
- package/templates/instance/src/app/(system)/api/prototype-dirty/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/login/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/logout/route.ts +4 -0
- package/templates/instance/src/app/(system)/auth/status/route.ts +4 -0
- package/templates/instance/src/app/(system)/layout.tsx +20 -0
- package/templates/instance/src/app/(system)/login/page.tsx +14 -0
- package/templates/instance/src/app/(system)/page.tsx +33 -0
- package/templates/instance/src/app/api/items/[itemId]/route.ts +21 -0
- package/templates/instance/src/app/api/items/route.ts +11 -0
- package/templates/instance/src/app/layout.tsx +56 -0
- package/templates/instance/src/bonfire.ts +25 -0
- package/templates/instance/src/lib/items/gateway-schemas.ts +100 -0
- package/templates/instance/src/lib/items/server.ts +220 -0
- package/templates/instance/src/proxy.ts +8 -0
package/README.md
CHANGED
|
@@ -33,6 +33,32 @@ The command never invokes GitHub CLI, loads application configuration, or
|
|
|
33
33
|
prints the secret. It exits with `0` on success, `1` on validation or
|
|
34
34
|
filesystem failure, and `2` for invalid command syntax.
|
|
35
35
|
|
|
36
|
+
## Converting a Next.js scaffold
|
|
37
|
+
|
|
38
|
+
Convert a compatible Next.js App Router scaffold in place. This command
|
|
39
|
+
intentionally generates an eBay-configured application, including the eBay
|
|
40
|
+
preset, corporate GitHub hostname, and deployment defaults:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
bonfire convert \
|
|
44
|
+
--root /path/to/app \
|
|
45
|
+
--app-id my-bonfire-app
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When running from the Bonfire monorepo, use the workspace package directly
|
|
49
|
+
(after building the CLI):
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
pnpm --filter @ebay-dt/bonfire-cli exec node dist/bin.js convert \
|
|
53
|
+
--root /path/to/app \
|
|
54
|
+
--app-id my-bonfire-app \
|
|
55
|
+
--name "My Bonfire App"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`--root` and `--app-id` are required. The display name defaults to a humanized app ID (`my-bonfire-app` becomes `My Bonfire App`). Hostname defaults to `github.corp.ebay.com` and the development port defaults to `1995`. Data routes are included by default; use `--without-data` to omit the eBay item gateway adapter. Use `--dry-run` to inspect the plan and `--skip-install` to leave dependency installation and type generation to the caller. Existing customized files and conflicting settings stop the conversion rather than being overwritten.
|
|
59
|
+
|
|
60
|
+
The normal conversion performs `pnpm install`, formats changed files, runs `next typegen`, and synchronizes generated repository context. It does not create GitHub/Vercel projects, authenticate a machine, or start the development server.
|
|
61
|
+
|
|
36
62
|
## Context repository operations
|
|
37
63
|
|
|
38
64
|
Use the context commands explicitly from an instance repository:
|
package/dist/command.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ContextInstallCheckResult, type ContextInstallResult, type ContextInstallOptions } from "@ebay-dt/bonfire-context";
|
|
2
2
|
import { type BranchPrepareDependencies } from "./branch-prepare.js";
|
|
3
|
+
import { type ConvertOptions } from "./convert.js";
|
|
3
4
|
export declare class CliSyntaxError extends Error {
|
|
4
5
|
constructor(message: string);
|
|
5
6
|
}
|
|
@@ -20,7 +21,10 @@ export type ContextCommandOptions = {
|
|
|
20
21
|
appId: string;
|
|
21
22
|
check: boolean;
|
|
22
23
|
};
|
|
23
|
-
type
|
|
24
|
+
export type ConvertCommandOptions = ConvertOptions & {
|
|
25
|
+
command: "convert";
|
|
26
|
+
};
|
|
27
|
+
type CliCommandOptions = PrepareCommandOptions | BranchPrepareCommandOptions | ContextCommandOptions | ConvertCommandOptions;
|
|
24
28
|
export type CliDependencies = BranchPrepareDependencies & {
|
|
25
29
|
contextInstall?: (options: ContextInstallOptions) => Promise<ContextInstallResult>;
|
|
26
30
|
contextInstallCheck?: (options: ContextInstallOptions) => Promise<ContextInstallCheckResult>;
|
package/dist/command.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE3B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAE7B,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,OAAO,EAAE,SAAS,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEN,KAAK,iBAAiB,GACpB,qBAAqB,GAAG,2BAA2B,GAAG,qBAAqB,CAAC;AAE9E,MAAM,MAAM,eAAe,GAAG,yBAAyB,GAAG;IACxD,cAAc,CAAC,EAAE,CACf,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,CACpB,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACzC,CAAC;AAmJF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CASpE;AA+ED,wBAAsB,GAAG,CACvB,IAAI,GAAE,SAAS,MAAM,EAA0B,EAC/C,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,CAiDjB","sourcesContent":["import {\n checkContext,\n checkContextInstallation,\n installContext,\n syncContext,\n type ContextInstallCheckResult,\n type ContextInstallResult,\n type ContextInstallOptions,\n type ContextSyncResult,\n} from \"@ebay-dt/bonfire-context\";\nimport { prepare, PrepareError } from \"./prepare.js\";\nimport {\n BranchPrepareError,\n runBranchPrepare,\n type BranchPrepareDependencies,\n} from \"./branch-prepare.js\";\n\nexport class CliSyntaxError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliSyntaxError\";\n }\n}\n\nexport type PrepareCommandOptions = {\n root: string;\n};\n\nexport type BranchPrepareCommandOptions = {\n root: string;\n hostname: string;\n};\n\nexport type ContextCommandOptions =\n | {\n context: \"sync\";\n root: string;\n check: boolean;\n }\n | {\n context: \"install\";\n root: string;\n appId: string;\n check: boolean;\n };\n\ntype CliCommandOptions =\n PrepareCommandOptions | BranchPrepareCommandOptions | ContextCommandOptions;\n\nexport type CliDependencies = BranchPrepareDependencies & {\n contextInstall?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallResult>;\n contextInstallCheck?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallCheckResult>;\n};\n\nfunction parseRootOption(\n argv: readonly string[],\n start: number,\n): { root: string; nextIndex: number } {\n const value = argv[start + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --root option requires a value\");\n }\n return { root: value, nextIndex: start + 2 };\n}\n\nfunction parsePrepareArgs(argv: readonly string[]): PrepareCommandOptions {\n let root = process.cwd();\n let rootProvided = false;\n for (let index = 1; index < argv.length;) {\n if (argv[index] !== \"--root\") {\n throw new CliSyntaxError(`Unknown option: ${argv[index]}`);\n }\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n }\n return { root };\n}\n\nfunction parseContextArgs(argv: readonly string[]): ContextCommandOptions {\n const context = argv[1];\n if (context !== \"sync\" && context !== \"install\") {\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n\n let root = process.cwd();\n let appId: string | undefined;\n let check = false;\n let rootProvided = false;\n let appIdProvided = false;\n let checkProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--check\") {\n if (checkProvided) {\n throw new CliSyntaxError(\n \"The --check option may only be provided once\",\n );\n }\n check = true;\n checkProvided = true;\n index += 1;\n continue;\n }\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--app-id\") {\n if (appIdProvided) {\n throw new CliSyntaxError(\n \"The --app-id option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --app-id option requires a value\");\n }\n appId = value;\n appIdProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (context === \"install\") {\n if (appId === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n return { context, root, appId, check };\n }\n if (appId !== undefined) {\n throw new CliSyntaxError(\"The --app-id option is only valid for install\");\n }\n return { context, root, check };\n}\n\nfunction parseBranchPrepareArgs(\n argv: readonly string[],\n): BranchPrepareCommandOptions {\n let root = process.cwd();\n let hostname: string | undefined;\n let rootProvided = false;\n let hostnameProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--hostname\") {\n if (hostnameProvided) {\n throw new CliSyntaxError(\n \"The --hostname option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --hostname option requires a value\");\n }\n hostname = value;\n hostnameProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (hostname === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n }\n return { root, hostname };\n}\n\nexport function parseArgs(argv: readonly string[]): CliCommandOptions {\n if (argv[0] === \"context\") return parseContextArgs(argv);\n if (argv[0] === \"prepare\") return parsePrepareArgs(argv);\n if (argv[0] === \"branch\" && argv[1] === \"prepare\") {\n return parseBranchPrepareArgs(argv);\n }\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n}\n\nfunction printContextIssues(result: {\n issues: readonly { kind: string; path: string }[];\n}) {\n for (const issue of result.issues) {\n console.error(`- ${issue.kind}: ${issue.path}`);\n }\n}\n\nfunction printContextSyncSummary(result: ContextSyncResult) {\n if (result.written.length > 0) {\n console.log(`Written: ${result.written.join(\", \")}`);\n }\n if (result.removed.length > 0) {\n console.log(`Removed: ${result.removed.join(\", \")}`);\n }\n}\n\nasync function runContextCommand(\n options: ContextCommandOptions,\n dependencies: CliDependencies,\n): Promise<number> {\n if (options.context === \"install\") {\n if (options.check) {\n const result = await (\n dependencies.contextInstallCheck ?? checkContextInstallation\n )({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n if (!result.ok) {\n console.error(\"Context install check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context install check passed.\");\n return 0;\n }\n const result = await (dependencies.contextInstall ?? installContext)({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n const skills = result.skills.map(\n (skill) => `${skill.name} (${skill.status})`,\n );\n const mcp = result.mcp.map((server) => `${server.name} (${server.status})`);\n console.log(\n skills.length > 0\n ? `Context skills: ${skills.join(\", \")}`\n : \"No context skills configured.\",\n );\n if (mcp.length > 0) console.log(`Context MCP: ${mcp.join(\", \")}`);\n console.log(\"Context install complete.\");\n return 0;\n }\n\n if (options.check) {\n const result = await checkContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context sync check passed.\");\n return 0;\n }\n\n const result = await syncContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync failed:\");\n printContextIssues(result);\n return 1;\n }\n printContextSyncSummary(result);\n console.log(\"Context sync complete.\");\n return 0;\n}\n\nexport async function run(\n argv: readonly string[] = process.argv.slice(2),\n dependencies: CliDependencies = {},\n): Promise<number> {\n const isBranchPrepare = argv[0] === \"branch\";\n const isContext = argv[0] === \"context\";\n try {\n const options = parseArgs(argv);\n if (\"context\" in options) {\n return await runContextCommand(options, dependencies);\n }\n if (\"hostname\" in options) {\n return await runBranchPrepare(options, dependencies);\n }\n\n const result = await prepare(options);\n const descriptions = {\n environment: \"the process environment\",\n existing: \".env.local\",\n generated: \"a newly generated value in .env.local\",\n } as const;\n console.log(\n `Bonfire preparation complete; BF_SESSION_SECRET came from ${descriptions[result.source]}.`,\n );\n return 0;\n } catch (error) {\n if (error instanceof CliSyntaxError) {\n console.error(`bonfire: ${error.message}`);\n return 2;\n }\n if (isContext) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire context failed: ${message}`);\n return 1;\n }\n if (isBranchPrepare) {\n const message =\n error instanceof BranchPrepareError\n ? error.message\n : \"Unable to load branch preparation configuration.\";\n console.error(`bonfire branch prepare failed: ${message}`);\n return 1;\n }\n\n const message =\n error instanceof PrepareError || error instanceof Error\n ? error.message\n : \"an unknown error occurred\";\n console.error(`bonfire prepare failed: ${message}`);\n return 1;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE3B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAEtB,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IACE,OAAO,EAAE,SAAS,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEN,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5E,KAAK,iBAAiB,GAClB,qBAAqB,GACrB,2BAA2B,GAC3B,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,eAAe,GAAG,yBAAyB,GAAG;IACxD,cAAc,CAAC,EAAE,CACf,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,CACpB,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACzC,CAAC;AAiPF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CAUpE;AA+ED,wBAAsB,GAAG,CACvB,IAAI,GAAE,SAAS,MAAM,EAA0B,EAC/C,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,CA4DjB","sourcesContent":["import {\n checkContext,\n checkContextInstallation,\n installContext,\n syncContext,\n type ContextInstallCheckResult,\n type ContextInstallResult,\n type ContextInstallOptions,\n type ContextSyncResult,\n} from \"@ebay-dt/bonfire-context\";\nimport { prepare, PrepareError } from \"./prepare.js\";\nimport {\n BranchPrepareError,\n runBranchPrepare,\n type BranchPrepareDependencies,\n} from \"./branch-prepare.js\";\nimport {\n convert,\n convertDefaults,\n defaultConvertOptions,\n humanizeAppId,\n type ConvertOptions,\n} from \"./convert.js\";\n\nexport class CliSyntaxError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliSyntaxError\";\n }\n}\n\nexport type PrepareCommandOptions = {\n root: string;\n};\n\nexport type BranchPrepareCommandOptions = {\n root: string;\n hostname: string;\n};\n\nexport type ContextCommandOptions =\n | {\n context: \"sync\";\n root: string;\n check: boolean;\n }\n | {\n context: \"install\";\n root: string;\n appId: string;\n check: boolean;\n };\n\nexport type ConvertCommandOptions = ConvertOptions & { command: \"convert\" };\n\ntype CliCommandOptions =\n | PrepareCommandOptions\n | BranchPrepareCommandOptions\n | ContextCommandOptions\n | ConvertCommandOptions;\n\nexport type CliDependencies = BranchPrepareDependencies & {\n contextInstall?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallResult>;\n contextInstallCheck?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallCheckResult>;\n};\n\nfunction parseRootOption(\n argv: readonly string[],\n start: number,\n): { root: string; nextIndex: number } {\n const value = argv[start + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --root option requires a value\");\n }\n return { root: value, nextIndex: start + 2 };\n}\n\nfunction parsePrepareArgs(argv: readonly string[]): PrepareCommandOptions {\n let root = process.cwd();\n let rootProvided = false;\n for (let index = 1; index < argv.length;) {\n if (argv[index] !== \"--root\") {\n throw new CliSyntaxError(`Unknown option: ${argv[index]}`);\n }\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n }\n return { root };\n}\n\nfunction parseContextArgs(argv: readonly string[]): ContextCommandOptions {\n const context = argv[1];\n if (context !== \"sync\" && context !== \"install\") {\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n\n let root = process.cwd();\n let appId: string | undefined;\n let check = false;\n let rootProvided = false;\n let appIdProvided = false;\n let checkProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--check\") {\n if (checkProvided) {\n throw new CliSyntaxError(\n \"The --check option may only be provided once\",\n );\n }\n check = true;\n checkProvided = true;\n index += 1;\n continue;\n }\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--app-id\") {\n if (appIdProvided) {\n throw new CliSyntaxError(\n \"The --app-id option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --app-id option requires a value\");\n }\n appId = value;\n appIdProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (context === \"install\") {\n if (appId === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n return { context, root, appId, check };\n }\n if (appId !== undefined) {\n throw new CliSyntaxError(\"The --app-id option is only valid for install\");\n }\n return { context, root, check };\n}\n\nfunction parseConvertArgs(argv: readonly string[]): ConvertCommandOptions {\n const defaults = defaultConvertOptions();\n let root: string | undefined;\n let appId: string | undefined;\n let name: string | undefined;\n let packageName = defaults.packageName;\n let hostname = convertDefaults.hostname;\n let port = convertDefaults.port;\n let deploymentEndpoint = convertDefaults.deploymentEndpoint;\n let withoutData = false;\n let dryRun = false;\n let skipInstall = false;\n const seen = new Set<string>();\n\n for (let index = 1; index < argv.length;) {\n const option = argv[index];\n if (!option) throw new CliSyntaxError(\"Missing convert option\");\n if (\n option === \"--without-data\" ||\n option === \"--dry-run\" ||\n option === \"--skip-install\"\n ) {\n if (seen.has(option))\n throw new CliSyntaxError(`${option} may only be provided once`);\n seen.add(option);\n if (option === \"--without-data\") withoutData = true;\n if (option === \"--dry-run\") dryRun = true;\n if (option === \"--skip-install\") skipInstall = true;\n index += 1;\n continue;\n }\n const setters: Record<string, (value: string) => void> = {\n \"--root\": (value) => {\n root = value;\n },\n \"--app-id\": (value) => {\n appId = value;\n },\n \"--name\": (value) => {\n name = value;\n },\n \"--package-name\": (value) => {\n packageName = value;\n },\n \"--hostname\": (value) => {\n hostname = value;\n },\n \"--deployment-endpoint\": (value) => {\n deploymentEndpoint = value;\n },\n \"--port\": (value) => {\n if (\n !/^\\d+$/u.test(value) ||\n Number(value) < 1 ||\n Number(value) > 65535\n ) {\n throw new CliSyntaxError(\n \"--port must be a number between 1 and 65535\",\n );\n }\n port = Number(value);\n },\n };\n const setter = setters[option];\n if (!setter) throw new CliSyntaxError(`Unknown option: ${option}`);\n if (seen.has(option))\n throw new CliSyntaxError(`${option} may only be provided once`);\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\"))\n throw new CliSyntaxError(`${option} requires a value`);\n seen.add(option);\n setter(value);\n index += 2;\n }\n if (!root || !appId) {\n throw new CliSyntaxError(\n \"Usage: bonfire convert --root <next-app> --app-id <app-id> [--name <display-name>] [--hostname <hostname>] [--port <port>] [--without-data] [--dry-run] [--skip-install]\",\n );\n }\n return {\n command: \"convert\",\n root,\n appId,\n name: name ?? humanizeAppId(appId),\n packageName,\n hostname,\n port,\n deploymentEndpoint,\n withItemData: !withoutData,\n dryRun,\n skipInstall,\n };\n}\n\nfunction parseBranchPrepareArgs(\n argv: readonly string[],\n): BranchPrepareCommandOptions {\n let root = process.cwd();\n let hostname: string | undefined;\n let rootProvided = false;\n let hostnameProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--hostname\") {\n if (hostnameProvided) {\n throw new CliSyntaxError(\n \"The --hostname option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --hostname option requires a value\");\n }\n hostname = value;\n hostnameProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (hostname === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n }\n return { root, hostname };\n}\n\nexport function parseArgs(argv: readonly string[]): CliCommandOptions {\n if (argv[0] === \"convert\") return parseConvertArgs(argv);\n if (argv[0] === \"context\") return parseContextArgs(argv);\n if (argv[0] === \"prepare\") return parsePrepareArgs(argv);\n if (argv[0] === \"branch\" && argv[1] === \"prepare\") {\n return parseBranchPrepareArgs(argv);\n }\n throw new CliSyntaxError(\n \"Usage: bonfire convert --root <next-app> --app-id <app-id> [options] | bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n}\n\nfunction printContextIssues(result: {\n issues: readonly { kind: string; path: string }[];\n}) {\n for (const issue of result.issues) {\n console.error(`- ${issue.kind}: ${issue.path}`);\n }\n}\n\nfunction printContextSyncSummary(result: ContextSyncResult) {\n if (result.written.length > 0) {\n console.log(`Written: ${result.written.join(\", \")}`);\n }\n if (result.removed.length > 0) {\n console.log(`Removed: ${result.removed.join(\", \")}`);\n }\n}\n\nasync function runContextCommand(\n options: ContextCommandOptions,\n dependencies: CliDependencies,\n): Promise<number> {\n if (options.context === \"install\") {\n if (options.check) {\n const result = await (\n dependencies.contextInstallCheck ?? checkContextInstallation\n )({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n if (!result.ok) {\n console.error(\"Context install check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context install check passed.\");\n return 0;\n }\n const result = await (dependencies.contextInstall ?? installContext)({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n const skills = result.skills.map(\n (skill) => `${skill.name} (${skill.status})`,\n );\n const mcp = result.mcp.map((server) => `${server.name} (${server.status})`);\n console.log(\n skills.length > 0\n ? `Context skills: ${skills.join(\", \")}`\n : \"No context skills configured.\",\n );\n if (mcp.length > 0) console.log(`Context MCP: ${mcp.join(\", \")}`);\n console.log(\"Context install complete.\");\n return 0;\n }\n\n if (options.check) {\n const result = await checkContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context sync check passed.\");\n return 0;\n }\n\n const result = await syncContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync failed:\");\n printContextIssues(result);\n return 1;\n }\n printContextSyncSummary(result);\n console.log(\"Context sync complete.\");\n return 0;\n}\n\nexport async function run(\n argv: readonly string[] = process.argv.slice(2),\n dependencies: CliDependencies = {},\n): Promise<number> {\n const isBranchPrepare = argv[0] === \"branch\";\n const isContext = argv[0] === \"context\";\n const isConvert = argv[0] === \"convert\";\n try {\n const options = parseArgs(argv);\n if (\"context\" in options) {\n return await runContextCommand(options, dependencies);\n }\n if (isConvert && \"command\" in options) {\n await convert(options);\n return 0;\n }\n if (\"hostname\" in options) {\n return await runBranchPrepare(options, dependencies);\n }\n\n const result = await prepare(options);\n const descriptions = {\n environment: \"the process environment\",\n existing: \".env.local\",\n generated: \"a newly generated value in .env.local\",\n } as const;\n console.log(\n `Bonfire preparation complete; BF_SESSION_SECRET came from ${descriptions[result.source]}.`,\n );\n return 0;\n } catch (error) {\n if (error instanceof CliSyntaxError) {\n console.error(`bonfire: ${error.message}`);\n return 2;\n }\n if (isContext) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire context failed: ${message}`);\n return 1;\n }\n if (isBranchPrepare) {\n const message =\n error instanceof BranchPrepareError\n ? error.message\n : \"Unable to load branch preparation configuration.\";\n console.error(`bonfire branch prepare failed: ${message}`);\n return 1;\n }\n if (isConvert) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire convert failed: ${message}`);\n return 1;\n }\n\n const message =\n error instanceof PrepareError || error instanceof Error\n ? error.message\n : \"an unknown error occurred\";\n console.error(`bonfire prepare failed: ${message}`);\n return 1;\n }\n}\n"]}
|
package/dist/command.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { checkContext, checkContextInstallation, installContext, syncContext, } from "@ebay-dt/bonfire-context";
|
|
2
2
|
import { prepare, PrepareError } from "./prepare.js";
|
|
3
3
|
import { BranchPrepareError, runBranchPrepare, } from "./branch-prepare.js";
|
|
4
|
+
import { convert, convertDefaults, defaultConvertOptions, humanizeAppId, } from "./convert.js";
|
|
4
5
|
export class CliSyntaxError extends Error {
|
|
5
6
|
constructor(message) {
|
|
6
7
|
super(message);
|
|
@@ -89,6 +90,95 @@ function parseContextArgs(argv) {
|
|
|
89
90
|
}
|
|
90
91
|
return { context, root, check };
|
|
91
92
|
}
|
|
93
|
+
function parseConvertArgs(argv) {
|
|
94
|
+
const defaults = defaultConvertOptions();
|
|
95
|
+
let root;
|
|
96
|
+
let appId;
|
|
97
|
+
let name;
|
|
98
|
+
let packageName = defaults.packageName;
|
|
99
|
+
let hostname = convertDefaults.hostname;
|
|
100
|
+
let port = convertDefaults.port;
|
|
101
|
+
let deploymentEndpoint = convertDefaults.deploymentEndpoint;
|
|
102
|
+
let withoutData = false;
|
|
103
|
+
let dryRun = false;
|
|
104
|
+
let skipInstall = false;
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
for (let index = 1; index < argv.length;) {
|
|
107
|
+
const option = argv[index];
|
|
108
|
+
if (!option)
|
|
109
|
+
throw new CliSyntaxError("Missing convert option");
|
|
110
|
+
if (option === "--without-data" ||
|
|
111
|
+
option === "--dry-run" ||
|
|
112
|
+
option === "--skip-install") {
|
|
113
|
+
if (seen.has(option))
|
|
114
|
+
throw new CliSyntaxError(`${option} may only be provided once`);
|
|
115
|
+
seen.add(option);
|
|
116
|
+
if (option === "--without-data")
|
|
117
|
+
withoutData = true;
|
|
118
|
+
if (option === "--dry-run")
|
|
119
|
+
dryRun = true;
|
|
120
|
+
if (option === "--skip-install")
|
|
121
|
+
skipInstall = true;
|
|
122
|
+
index += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const setters = {
|
|
126
|
+
"--root": (value) => {
|
|
127
|
+
root = value;
|
|
128
|
+
},
|
|
129
|
+
"--app-id": (value) => {
|
|
130
|
+
appId = value;
|
|
131
|
+
},
|
|
132
|
+
"--name": (value) => {
|
|
133
|
+
name = value;
|
|
134
|
+
},
|
|
135
|
+
"--package-name": (value) => {
|
|
136
|
+
packageName = value;
|
|
137
|
+
},
|
|
138
|
+
"--hostname": (value) => {
|
|
139
|
+
hostname = value;
|
|
140
|
+
},
|
|
141
|
+
"--deployment-endpoint": (value) => {
|
|
142
|
+
deploymentEndpoint = value;
|
|
143
|
+
},
|
|
144
|
+
"--port": (value) => {
|
|
145
|
+
if (!/^\d+$/u.test(value) ||
|
|
146
|
+
Number(value) < 1 ||
|
|
147
|
+
Number(value) > 65535) {
|
|
148
|
+
throw new CliSyntaxError("--port must be a number between 1 and 65535");
|
|
149
|
+
}
|
|
150
|
+
port = Number(value);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
const setter = setters[option];
|
|
154
|
+
if (!setter)
|
|
155
|
+
throw new CliSyntaxError(`Unknown option: ${option}`);
|
|
156
|
+
if (seen.has(option))
|
|
157
|
+
throw new CliSyntaxError(`${option} may only be provided once`);
|
|
158
|
+
const value = argv[index + 1];
|
|
159
|
+
if (!value || value.startsWith("--"))
|
|
160
|
+
throw new CliSyntaxError(`${option} requires a value`);
|
|
161
|
+
seen.add(option);
|
|
162
|
+
setter(value);
|
|
163
|
+
index += 2;
|
|
164
|
+
}
|
|
165
|
+
if (!root || !appId) {
|
|
166
|
+
throw new CliSyntaxError("Usage: bonfire convert --root <next-app> --app-id <app-id> [--name <display-name>] [--hostname <hostname>] [--port <port>] [--without-data] [--dry-run] [--skip-install]");
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
command: "convert",
|
|
170
|
+
root,
|
|
171
|
+
appId,
|
|
172
|
+
name: name ?? humanizeAppId(appId),
|
|
173
|
+
packageName,
|
|
174
|
+
hostname,
|
|
175
|
+
port,
|
|
176
|
+
deploymentEndpoint,
|
|
177
|
+
withItemData: !withoutData,
|
|
178
|
+
dryRun,
|
|
179
|
+
skipInstall,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
92
182
|
function parseBranchPrepareArgs(argv) {
|
|
93
183
|
let root = process.cwd();
|
|
94
184
|
let hostname;
|
|
@@ -127,6 +217,8 @@ function parseBranchPrepareArgs(argv) {
|
|
|
127
217
|
return { root, hostname };
|
|
128
218
|
}
|
|
129
219
|
export function parseArgs(argv) {
|
|
220
|
+
if (argv[0] === "convert")
|
|
221
|
+
return parseConvertArgs(argv);
|
|
130
222
|
if (argv[0] === "context")
|
|
131
223
|
return parseContextArgs(argv);
|
|
132
224
|
if (argv[0] === "prepare")
|
|
@@ -134,7 +226,7 @@ export function parseArgs(argv) {
|
|
|
134
226
|
if (argv[0] === "branch" && argv[1] === "prepare") {
|
|
135
227
|
return parseBranchPrepareArgs(argv);
|
|
136
228
|
}
|
|
137
|
-
throw new CliSyntaxError("Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>");
|
|
229
|
+
throw new CliSyntaxError("Usage: bonfire convert --root <next-app> --app-id <app-id> [options] | bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>");
|
|
138
230
|
}
|
|
139
231
|
function printContextIssues(result) {
|
|
140
232
|
for (const issue of result.issues) {
|
|
@@ -201,11 +293,16 @@ async function runContextCommand(options, dependencies) {
|
|
|
201
293
|
export async function run(argv = process.argv.slice(2), dependencies = {}) {
|
|
202
294
|
const isBranchPrepare = argv[0] === "branch";
|
|
203
295
|
const isContext = argv[0] === "context";
|
|
296
|
+
const isConvert = argv[0] === "convert";
|
|
204
297
|
try {
|
|
205
298
|
const options = parseArgs(argv);
|
|
206
299
|
if ("context" in options) {
|
|
207
300
|
return await runContextCommand(options, dependencies);
|
|
208
301
|
}
|
|
302
|
+
if (isConvert && "command" in options) {
|
|
303
|
+
await convert(options);
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
209
306
|
if ("hostname" in options) {
|
|
210
307
|
return await runBranchPrepare(options, dependencies);
|
|
211
308
|
}
|
|
@@ -235,6 +332,11 @@ export async function run(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
235
332
|
console.error(`bonfire branch prepare failed: ${message}`);
|
|
236
333
|
return 1;
|
|
237
334
|
}
|
|
335
|
+
if (isConvert) {
|
|
336
|
+
const message = error instanceof Error ? error.message : "an unknown error occurred";
|
|
337
|
+
console.error(`bonfire convert failed: ${message}`);
|
|
338
|
+
return 1;
|
|
339
|
+
}
|
|
238
340
|
const message = error instanceof PrepareError || error instanceof Error
|
|
239
341
|
? error.message
|
|
240
342
|
: "an unknown error occurred";
|
package/dist/command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n checkContext,\n checkContextInstallation,\n installContext,\n syncContext,\n type ContextInstallCheckResult,\n type ContextInstallResult,\n type ContextInstallOptions,\n type ContextSyncResult,\n} from \"@ebay-dt/bonfire-context\";\nimport { prepare, PrepareError } from \"./prepare.js\";\nimport {\n BranchPrepareError,\n runBranchPrepare,\n type BranchPrepareDependencies,\n} from \"./branch-prepare.js\";\n\nexport class CliSyntaxError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliSyntaxError\";\n }\n}\n\nexport type PrepareCommandOptions = {\n root: string;\n};\n\nexport type BranchPrepareCommandOptions = {\n root: string;\n hostname: string;\n};\n\nexport type ContextCommandOptions =\n | {\n context: \"sync\";\n root: string;\n check: boolean;\n }\n | {\n context: \"install\";\n root: string;\n appId: string;\n check: boolean;\n };\n\ntype CliCommandOptions =\n PrepareCommandOptions | BranchPrepareCommandOptions | ContextCommandOptions;\n\nexport type CliDependencies = BranchPrepareDependencies & {\n contextInstall?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallResult>;\n contextInstallCheck?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallCheckResult>;\n};\n\nfunction parseRootOption(\n argv: readonly string[],\n start: number,\n): { root: string; nextIndex: number } {\n const value = argv[start + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --root option requires a value\");\n }\n return { root: value, nextIndex: start + 2 };\n}\n\nfunction parsePrepareArgs(argv: readonly string[]): PrepareCommandOptions {\n let root = process.cwd();\n let rootProvided = false;\n for (let index = 1; index < argv.length;) {\n if (argv[index] !== \"--root\") {\n throw new CliSyntaxError(`Unknown option: ${argv[index]}`);\n }\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n }\n return { root };\n}\n\nfunction parseContextArgs(argv: readonly string[]): ContextCommandOptions {\n const context = argv[1];\n if (context !== \"sync\" && context !== \"install\") {\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n\n let root = process.cwd();\n let appId: string | undefined;\n let check = false;\n let rootProvided = false;\n let appIdProvided = false;\n let checkProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--check\") {\n if (checkProvided) {\n throw new CliSyntaxError(\n \"The --check option may only be provided once\",\n );\n }\n check = true;\n checkProvided = true;\n index += 1;\n continue;\n }\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--app-id\") {\n if (appIdProvided) {\n throw new CliSyntaxError(\n \"The --app-id option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --app-id option requires a value\");\n }\n appId = value;\n appIdProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (context === \"install\") {\n if (appId === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n return { context, root, appId, check };\n }\n if (appId !== undefined) {\n throw new CliSyntaxError(\"The --app-id option is only valid for install\");\n }\n return { context, root, check };\n}\n\nfunction parseBranchPrepareArgs(\n argv: readonly string[],\n): BranchPrepareCommandOptions {\n let root = process.cwd();\n let hostname: string | undefined;\n let rootProvided = false;\n let hostnameProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--hostname\") {\n if (hostnameProvided) {\n throw new CliSyntaxError(\n \"The --hostname option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --hostname option requires a value\");\n }\n hostname = value;\n hostnameProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (hostname === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n }\n return { root, hostname };\n}\n\nexport function parseArgs(argv: readonly string[]): CliCommandOptions {\n if (argv[0] === \"context\") return parseContextArgs(argv);\n if (argv[0] === \"prepare\") return parsePrepareArgs(argv);\n if (argv[0] === \"branch\" && argv[1] === \"prepare\") {\n return parseBranchPrepareArgs(argv);\n }\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n}\n\nfunction printContextIssues(result: {\n issues: readonly { kind: string; path: string }[];\n}) {\n for (const issue of result.issues) {\n console.error(`- ${issue.kind}: ${issue.path}`);\n }\n}\n\nfunction printContextSyncSummary(result: ContextSyncResult) {\n if (result.written.length > 0) {\n console.log(`Written: ${result.written.join(\", \")}`);\n }\n if (result.removed.length > 0) {\n console.log(`Removed: ${result.removed.join(\", \")}`);\n }\n}\n\nasync function runContextCommand(\n options: ContextCommandOptions,\n dependencies: CliDependencies,\n): Promise<number> {\n if (options.context === \"install\") {\n if (options.check) {\n const result = await (\n dependencies.contextInstallCheck ?? checkContextInstallation\n )({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n if (!result.ok) {\n console.error(\"Context install check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context install check passed.\");\n return 0;\n }\n const result = await (dependencies.contextInstall ?? installContext)({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n const skills = result.skills.map(\n (skill) => `${skill.name} (${skill.status})`,\n );\n const mcp = result.mcp.map((server) => `${server.name} (${server.status})`);\n console.log(\n skills.length > 0\n ? `Context skills: ${skills.join(\", \")}`\n : \"No context skills configured.\",\n );\n if (mcp.length > 0) console.log(`Context MCP: ${mcp.join(\", \")}`);\n console.log(\"Context install complete.\");\n return 0;\n }\n\n if (options.check) {\n const result = await checkContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context sync check passed.\");\n return 0;\n }\n\n const result = await syncContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync failed:\");\n printContextIssues(result);\n return 1;\n }\n printContextSyncSummary(result);\n console.log(\"Context sync complete.\");\n return 0;\n}\n\nexport async function run(\n argv: readonly string[] = process.argv.slice(2),\n dependencies: CliDependencies = {},\n): Promise<number> {\n const isBranchPrepare = argv[0] === \"branch\";\n const isContext = argv[0] === \"context\";\n try {\n const options = parseArgs(argv);\n if (\"context\" in options) {\n return await runContextCommand(options, dependencies);\n }\n if (\"hostname\" in options) {\n return await runBranchPrepare(options, dependencies);\n }\n\n const result = await prepare(options);\n const descriptions = {\n environment: \"the process environment\",\n existing: \".env.local\",\n generated: \"a newly generated value in .env.local\",\n } as const;\n console.log(\n `Bonfire preparation complete; BF_SESSION_SECRET came from ${descriptions[result.source]}.`,\n );\n return 0;\n } catch (error) {\n if (error instanceof CliSyntaxError) {\n console.error(`bonfire: ${error.message}`);\n return 2;\n }\n if (isContext) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire context failed: ${message}`);\n return 1;\n }\n if (isBranchPrepare) {\n const message =\n error instanceof BranchPrepareError\n ? error.message\n : \"Unable to load branch preparation configuration.\";\n console.error(`bonfire branch prepare failed: ${message}`);\n return 1;\n }\n\n const message =\n error instanceof PrepareError || error instanceof Error\n ? error.message\n : \"an unknown error occurred\";\n console.error(`bonfire prepare failed: ${message}`);\n return 1;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n checkContext,\n checkContextInstallation,\n installContext,\n syncContext,\n type ContextInstallCheckResult,\n type ContextInstallResult,\n type ContextInstallOptions,\n type ContextSyncResult,\n} from \"@ebay-dt/bonfire-context\";\nimport { prepare, PrepareError } from \"./prepare.js\";\nimport {\n BranchPrepareError,\n runBranchPrepare,\n type BranchPrepareDependencies,\n} from \"./branch-prepare.js\";\nimport {\n convert,\n convertDefaults,\n defaultConvertOptions,\n humanizeAppId,\n type ConvertOptions,\n} from \"./convert.js\";\n\nexport class CliSyntaxError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CliSyntaxError\";\n }\n}\n\nexport type PrepareCommandOptions = {\n root: string;\n};\n\nexport type BranchPrepareCommandOptions = {\n root: string;\n hostname: string;\n};\n\nexport type ContextCommandOptions =\n | {\n context: \"sync\";\n root: string;\n check: boolean;\n }\n | {\n context: \"install\";\n root: string;\n appId: string;\n check: boolean;\n };\n\nexport type ConvertCommandOptions = ConvertOptions & { command: \"convert\" };\n\ntype CliCommandOptions =\n | PrepareCommandOptions\n | BranchPrepareCommandOptions\n | ContextCommandOptions\n | ConvertCommandOptions;\n\nexport type CliDependencies = BranchPrepareDependencies & {\n contextInstall?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallResult>;\n contextInstallCheck?: (\n options: ContextInstallOptions,\n ) => Promise<ContextInstallCheckResult>;\n};\n\nfunction parseRootOption(\n argv: readonly string[],\n start: number,\n): { root: string; nextIndex: number } {\n const value = argv[start + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --root option requires a value\");\n }\n return { root: value, nextIndex: start + 2 };\n}\n\nfunction parsePrepareArgs(argv: readonly string[]): PrepareCommandOptions {\n let root = process.cwd();\n let rootProvided = false;\n for (let index = 1; index < argv.length;) {\n if (argv[index] !== \"--root\") {\n throw new CliSyntaxError(`Unknown option: ${argv[index]}`);\n }\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n }\n return { root };\n}\n\nfunction parseContextArgs(argv: readonly string[]): ContextCommandOptions {\n const context = argv[1];\n if (context !== \"sync\" && context !== \"install\") {\n throw new CliSyntaxError(\n \"Usage: bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n\n let root = process.cwd();\n let appId: string | undefined;\n let check = false;\n let rootProvided = false;\n let appIdProvided = false;\n let checkProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--check\") {\n if (checkProvided) {\n throw new CliSyntaxError(\n \"The --check option may only be provided once\",\n );\n }\n check = true;\n checkProvided = true;\n index += 1;\n continue;\n }\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--app-id\") {\n if (appIdProvided) {\n throw new CliSyntaxError(\n \"The --app-id option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --app-id option requires a value\");\n }\n appId = value;\n appIdProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (context === \"install\") {\n if (appId === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire context install [--check] --app-id <app-id> [--root <root>]\",\n );\n }\n return { context, root, appId, check };\n }\n if (appId !== undefined) {\n throw new CliSyntaxError(\"The --app-id option is only valid for install\");\n }\n return { context, root, check };\n}\n\nfunction parseConvertArgs(argv: readonly string[]): ConvertCommandOptions {\n const defaults = defaultConvertOptions();\n let root: string | undefined;\n let appId: string | undefined;\n let name: string | undefined;\n let packageName = defaults.packageName;\n let hostname = convertDefaults.hostname;\n let port = convertDefaults.port;\n let deploymentEndpoint = convertDefaults.deploymentEndpoint;\n let withoutData = false;\n let dryRun = false;\n let skipInstall = false;\n const seen = new Set<string>();\n\n for (let index = 1; index < argv.length;) {\n const option = argv[index];\n if (!option) throw new CliSyntaxError(\"Missing convert option\");\n if (\n option === \"--without-data\" ||\n option === \"--dry-run\" ||\n option === \"--skip-install\"\n ) {\n if (seen.has(option))\n throw new CliSyntaxError(`${option} may only be provided once`);\n seen.add(option);\n if (option === \"--without-data\") withoutData = true;\n if (option === \"--dry-run\") dryRun = true;\n if (option === \"--skip-install\") skipInstall = true;\n index += 1;\n continue;\n }\n const setters: Record<string, (value: string) => void> = {\n \"--root\": (value) => {\n root = value;\n },\n \"--app-id\": (value) => {\n appId = value;\n },\n \"--name\": (value) => {\n name = value;\n },\n \"--package-name\": (value) => {\n packageName = value;\n },\n \"--hostname\": (value) => {\n hostname = value;\n },\n \"--deployment-endpoint\": (value) => {\n deploymentEndpoint = value;\n },\n \"--port\": (value) => {\n if (\n !/^\\d+$/u.test(value) ||\n Number(value) < 1 ||\n Number(value) > 65535\n ) {\n throw new CliSyntaxError(\n \"--port must be a number between 1 and 65535\",\n );\n }\n port = Number(value);\n },\n };\n const setter = setters[option];\n if (!setter) throw new CliSyntaxError(`Unknown option: ${option}`);\n if (seen.has(option))\n throw new CliSyntaxError(`${option} may only be provided once`);\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\"))\n throw new CliSyntaxError(`${option} requires a value`);\n seen.add(option);\n setter(value);\n index += 2;\n }\n if (!root || !appId) {\n throw new CliSyntaxError(\n \"Usage: bonfire convert --root <next-app> --app-id <app-id> [--name <display-name>] [--hostname <hostname>] [--port <port>] [--without-data] [--dry-run] [--skip-install]\",\n );\n }\n return {\n command: \"convert\",\n root,\n appId,\n name: name ?? humanizeAppId(appId),\n packageName,\n hostname,\n port,\n deploymentEndpoint,\n withItemData: !withoutData,\n dryRun,\n skipInstall,\n };\n}\n\nfunction parseBranchPrepareArgs(\n argv: readonly string[],\n): BranchPrepareCommandOptions {\n let root = process.cwd();\n let hostname: string | undefined;\n let rootProvided = false;\n let hostnameProvided = false;\n\n for (let index = 2; index < argv.length;) {\n const option = argv[index];\n if (option === \"--root\") {\n if (rootProvided) {\n throw new CliSyntaxError(\"The --root option may only be provided once\");\n }\n const parsed = parseRootOption(argv, index);\n root = parsed.root;\n rootProvided = true;\n index = parsed.nextIndex;\n continue;\n }\n if (option === \"--hostname\") {\n if (hostnameProvided) {\n throw new CliSyntaxError(\n \"The --hostname option may only be provided once\",\n );\n }\n const value = argv[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new CliSyntaxError(\"The --hostname option requires a value\");\n }\n hostname = value;\n hostnameProvided = true;\n index += 2;\n continue;\n }\n throw new CliSyntaxError(`Unknown option: ${option}`);\n }\n\n if (hostname === undefined) {\n throw new CliSyntaxError(\n \"Usage: bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n }\n return { root, hostname };\n}\n\nexport function parseArgs(argv: readonly string[]): CliCommandOptions {\n if (argv[0] === \"convert\") return parseConvertArgs(argv);\n if (argv[0] === \"context\") return parseContextArgs(argv);\n if (argv[0] === \"prepare\") return parsePrepareArgs(argv);\n if (argv[0] === \"branch\" && argv[1] === \"prepare\") {\n return parseBranchPrepareArgs(argv);\n }\n throw new CliSyntaxError(\n \"Usage: bonfire convert --root <next-app> --app-id <app-id> [options] | bonfire context sync [--check] [--root <root>] | bonfire context install [--check] --app-id <app-id> [--root <root>] | bonfire prepare [--root <application-root>] | bonfire branch prepare --root <application-root> --hostname <hostname>\",\n );\n}\n\nfunction printContextIssues(result: {\n issues: readonly { kind: string; path: string }[];\n}) {\n for (const issue of result.issues) {\n console.error(`- ${issue.kind}: ${issue.path}`);\n }\n}\n\nfunction printContextSyncSummary(result: ContextSyncResult) {\n if (result.written.length > 0) {\n console.log(`Written: ${result.written.join(\", \")}`);\n }\n if (result.removed.length > 0) {\n console.log(`Removed: ${result.removed.join(\", \")}`);\n }\n}\n\nasync function runContextCommand(\n options: ContextCommandOptions,\n dependencies: CliDependencies,\n): Promise<number> {\n if (options.context === \"install\") {\n if (options.check) {\n const result = await (\n dependencies.contextInstallCheck ?? checkContextInstallation\n )({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n if (!result.ok) {\n console.error(\"Context install check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context install check passed.\");\n return 0;\n }\n const result = await (dependencies.contextInstall ?? installContext)({\n repositoryRoot: options.root,\n appId: options.appId,\n });\n const skills = result.skills.map(\n (skill) => `${skill.name} (${skill.status})`,\n );\n const mcp = result.mcp.map((server) => `${server.name} (${server.status})`);\n console.log(\n skills.length > 0\n ? `Context skills: ${skills.join(\", \")}`\n : \"No context skills configured.\",\n );\n if (mcp.length > 0) console.log(`Context MCP: ${mcp.join(\", \")}`);\n console.log(\"Context install complete.\");\n return 0;\n }\n\n if (options.check) {\n const result = await checkContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync check failed:\");\n printContextIssues(result);\n return 1;\n }\n console.log(\"Context sync check passed.\");\n return 0;\n }\n\n const result = await syncContext({ repositoryRoot: options.root });\n if (!result.ok) {\n console.error(\"Context sync failed:\");\n printContextIssues(result);\n return 1;\n }\n printContextSyncSummary(result);\n console.log(\"Context sync complete.\");\n return 0;\n}\n\nexport async function run(\n argv: readonly string[] = process.argv.slice(2),\n dependencies: CliDependencies = {},\n): Promise<number> {\n const isBranchPrepare = argv[0] === \"branch\";\n const isContext = argv[0] === \"context\";\n const isConvert = argv[0] === \"convert\";\n try {\n const options = parseArgs(argv);\n if (\"context\" in options) {\n return await runContextCommand(options, dependencies);\n }\n if (isConvert && \"command\" in options) {\n await convert(options);\n return 0;\n }\n if (\"hostname\" in options) {\n return await runBranchPrepare(options, dependencies);\n }\n\n const result = await prepare(options);\n const descriptions = {\n environment: \"the process environment\",\n existing: \".env.local\",\n generated: \"a newly generated value in .env.local\",\n } as const;\n console.log(\n `Bonfire preparation complete; BF_SESSION_SECRET came from ${descriptions[result.source]}.`,\n );\n return 0;\n } catch (error) {\n if (error instanceof CliSyntaxError) {\n console.error(`bonfire: ${error.message}`);\n return 2;\n }\n if (isContext) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire context failed: ${message}`);\n return 1;\n }\n if (isBranchPrepare) {\n const message =\n error instanceof BranchPrepareError\n ? error.message\n : \"Unable to load branch preparation configuration.\";\n console.error(`bonfire branch prepare failed: ${message}`);\n return 1;\n }\n if (isConvert) {\n const message =\n error instanceof Error ? error.message : \"an unknown error occurred\";\n console.error(`bonfire convert failed: ${message}`);\n return 1;\n }\n\n const message =\n error instanceof PrepareError || error instanceof Error\n ? error.message\n : \"an unknown error occurred\";\n console.error(`bonfire prepare failed: ${message}`);\n return 1;\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type ConvertOptions = Readonly<{
|
|
2
|
+
root: string;
|
|
3
|
+
appId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
packageName?: string;
|
|
6
|
+
hostname: string;
|
|
7
|
+
port: number;
|
|
8
|
+
deploymentEndpoint: string;
|
|
9
|
+
withItemData: boolean;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
skipInstall: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
type PackageJson = {
|
|
14
|
+
name?: unknown;
|
|
15
|
+
scripts?: Record<string, unknown>;
|
|
16
|
+
dependencies?: Record<string, string>;
|
|
17
|
+
devDependencies?: Record<string, string>;
|
|
18
|
+
engines?: Record<string, string>;
|
|
19
|
+
packageManager?: unknown;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
};
|
|
22
|
+
type PlannedChange = Readonly<{
|
|
23
|
+
kind: "add" | "update" | "remove";
|
|
24
|
+
path: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
}>;
|
|
27
|
+
type ConversionPlan = Readonly<{
|
|
28
|
+
root: string;
|
|
29
|
+
changes: readonly PlannedChange[];
|
|
30
|
+
writes: ReadonlyMap<string, string>;
|
|
31
|
+
removals: readonly string[];
|
|
32
|
+
packageJson: PackageJson;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function humanizeAppId(appId: string): string;
|
|
35
|
+
export declare class ConvertError extends Error {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
export declare function planConversion(options: ConvertOptions): Promise<ConversionPlan>;
|
|
39
|
+
export declare function convert(options: ConvertOptions): Promise<void>;
|
|
40
|
+
export declare function defaultConvertOptions(root?: string): ConvertOptions;
|
|
41
|
+
export declare const convertDefaults: {
|
|
42
|
+
hostname: string;
|
|
43
|
+
deploymentEndpoint: string;
|
|
44
|
+
port: number;
|
|
45
|
+
};
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=convert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AA0BA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAEH,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,aAAa,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,KAAK,cAAc,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC,CAAC;AAEH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,UAS1C;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AA2cD,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,cAAc,CAAC,CAsPzB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBA0DpD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,SAAgB,GAAG,cAAc,CAa1E;AAED,eAAO,MAAM,eAAe;;;;CAI3B,CAAC","sourcesContent":["import { execFile } from \"node:child_process\";\nimport {\n chmod,\n lstat,\n mkdir,\n readFile,\n readdir,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { fileURLToPath } from \"node:url\";\nimport { syncContext } from \"@ebay-dt/bonfire-context\";\n\nconst execFileAsync = promisify(execFile);\nconst TEMPLATE_ROOT = fileURLToPath(\n new URL(\"../templates/instance/\", import.meta.url),\n);\nconst BONFIRE_VERSION = \"0.1.0\";\nconst PRESET_VERSION = \"0.1.0\";\nconst DEFAULT_HOSTNAME = \"github.corp.ebay.com\";\nconst DEFAULT_DEPLOYMENT_ENDPOINT =\n \"https://dtsvc.vip.qa.ebay.com/dt/v1/bonfire-deploy\";\nconst DEFAULT_PORT = 1995;\n\nexport type ConvertOptions = Readonly<{\n root: string;\n appId: string;\n name: string;\n packageName?: string;\n hostname: string;\n port: number;\n deploymentEndpoint: string;\n withItemData: boolean;\n dryRun: boolean;\n skipInstall: boolean;\n}>;\n\ntype PackageJson = {\n name?: unknown;\n scripts?: Record<string, unknown>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n engines?: Record<string, string>;\n packageManager?: unknown;\n [key: string]: unknown;\n};\n\ntype PlannedChange = Readonly<{\n kind: \"add\" | \"update\" | \"remove\";\n path: string;\n reason: string;\n}>;\n\ntype ConversionPlan = Readonly<{\n root: string;\n changes: readonly PlannedChange[];\n writes: ReadonlyMap<string, string>;\n removals: readonly string[];\n packageJson: PackageJson;\n}>;\n\nexport function humanizeAppId(appId: string) {\n return appId\n .replace(/([a-z0-9])([A-Z])/gu, \"$1 $2\")\n .replace(/[._-]+/gu, \" \")\n .trim()\n .split(/\\s+/u)\n .filter(Boolean)\n .map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)\n .join(\" \");\n}\n\nexport class ConvertError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConvertError\";\n }\n}\n\nfunction displayPath(root: string, path: string) {\n return relative(root, path) || \".\";\n}\n\nfunction parseVersion(value: unknown, label: string) {\n if (typeof value !== \"string\") {\n throw new ConvertError(`${label} must be declared in package.json`);\n }\n const match = value.match(/(\\d+)\\.(\\d+)\\.(\\d+)/u);\n if (!match) {\n throw new ConvertError(`${label} has an unsupported version: ${value}`);\n }\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n };\n}\n\nfunction dependencyVersion(packageJson: PackageJson, name: string) {\n return (\n packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name]\n );\n}\n\nfunction validateStartingScaffold(packageJson: PackageJson) {\n const next = parseVersion(dependencyVersion(packageJson, \"next\"), \"next\");\n if (next.major !== 16 || next.minor < 2) {\n throw new ConvertError(\n `next must be 16.2 or newer within the 16.x line (found ${next.major}.${next.minor}.${next.patch})`,\n );\n }\n const react = parseVersion(dependencyVersion(packageJson, \"react\"), \"react\");\n if (react.major !== 19 || react.minor < 2) {\n throw new ConvertError(\n `react must be 19.2 or newer within the 19.x line (found ${react.major}.${react.minor}.${react.patch})`,\n );\n }\n if (!dependencyVersion(packageJson, \"typescript\")) {\n throw new ConvertError(\"typescript must be declared in package.json\");\n }\n\n if (Number(process.versions.node.split(\".\")[0]) < 24) {\n throw new ConvertError(\n \"Bonfire instance conversion requires Node.js 24 or newer\",\n );\n }\n return next;\n}\n\nasync function readJson(path: string) {\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as PackageJson;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new ConvertError(\n `${displayPath(dirname(path), path)} is not valid JSON`,\n );\n }\n throw error;\n }\n}\n\nasync function exists(path: string) {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if (\n error !== null &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return false;\n }\n throw error;\n }\n}\n\nasync function fileContent(path: string) {\n try {\n return await readFile(path, \"utf8\");\n } catch (error) {\n if (\n error !== null &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return undefined;\n }\n throw error;\n }\n}\n\nfunction addChange(\n changes: PlannedChange[],\n root: string,\n path: string,\n kind: PlannedChange[\"kind\"],\n reason: string,\n) {\n changes.push({ kind, path: displayPath(root, path), reason });\n}\n\nasync function planFile(\n root: string,\n path: string,\n content: string,\n reason: string,\n writes: Map<string, string>,\n changes: PlannedChange[],\n) {\n const existing = await fileContent(path);\n if (existing === undefined) {\n writes.set(path, content);\n addChange(changes, root, path, \"add\", reason);\n return;\n }\n if (existing === content) return;\n throw new ConvertError(\n `${displayPath(root, path)} already exists and differs from the Bonfire scaffold; resolve it before converting`,\n );\n}\n\nfunction looksLikeNextStarter(path: string, content: string) {\n if (path.endsWith(\"/page.tsx\")) {\n return /Create Next App|[Gg]et started|Looking for a starting point|create-next-app/u.test(\n content,\n );\n }\n if (path.endsWith(\"/layout.tsx\")) {\n return /next\\/font|Geist|Create Next App/u.test(content);\n }\n if (path.endsWith(\"/page.module.css\")) {\n return /\\.page|\\.main|\\.footer|\\.grid/u.test(content);\n }\n if (path.endsWith(\"/globals.css\")) {\n return /--background|--foreground|@import [\"']tailwindcss/u.test(content);\n }\n if (path.endsWith(\"/README.md\")) {\n return /bootstrapped with[\\s\\S]*create-next-app/u.test(content);\n }\n if (path.endsWith(\"/next.config.ts\")) {\n return /config options here/u.test(content);\n }\n return false;\n}\n\nfunction looksLikeStarterAsset(path: string, content: string) {\n const name = path.slice(path.lastIndexOf(\"/\") + 1);\n if (\n ![\"file.svg\", \"globe.svg\", \"next.svg\", \"vercel.svg\", \"window.svg\"].includes(\n name,\n )\n ) {\n return false;\n }\n return /<svg[\\s\\S]*viewBox=/u.test(content);\n}\n\nasync function planRemoveStarter(\n root: string,\n path: string,\n reason: string,\n removals: string[],\n changes: PlannedChange[],\n) {\n const content = await fileContent(path);\n if (content === undefined) return;\n if (\n content.includes(\"@ebay-dt/bonfire-next\") ||\n content.includes(\"A Bonfire instance for creating\") ||\n content.includes(\"outputFileTracingIncludes\")\n ) {\n return;\n }\n if (\n !looksLikeNextStarter(path, content) &&\n !looksLikeStarterAsset(path, content)\n ) {\n throw new ConvertError(\n `${displayPath(root, path)} is customized; it will not be removed automatically`,\n );\n }\n removals.push(path);\n addChange(changes, root, path, \"remove\", reason);\n}\n\nfunction replaceIdentity(content: string, options: ConvertOptions) {\n return content\n .replaceAll(\"design-sandbox-next\", () => options.appId)\n .replaceAll(\"Design Sandbox\", () => options.name)\n .replaceAll(\"github.corp.ebay.com\", () => options.hostname)\n .replaceAll(DEFAULT_DEPLOYMENT_ENDPOINT, () => options.deploymentEndpoint);\n}\n\nfunction withoutItemGuidance(content: string) {\n const start = content.indexOf(\"### Data fetching\");\n const end = content.indexOf(\"### Viewport sizing\");\n if (start < 0 || end < 0 || end <= start) return content;\n return `${content.slice(0, start)}${content.slice(end)}`;\n}\n\nasync function templateFiles(options: ConvertOptions) {\n const files: Array<{ relativePath: string; content: string }> = [];\n async function visit(directory: string) {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries.sort((left, right) =>\n left.name.localeCompare(right.name),\n )) {\n const path = join(directory, entry.name);\n const relativePath = relative(TEMPLATE_ROOT, path);\n if (\n !options.withItemData &&\n (relativePath.startsWith(\"src/app/api/items/\") ||\n relativePath.startsWith(\"src/lib/items/\") ||\n relativePath === \"context/skills/item-data/SKILL.md\")\n ) {\n continue;\n }\n if (entry.isDirectory()) {\n await visit(path);\n } else if (entry.isFile()) {\n let content = await readFile(path, \"utf8\");\n content = replaceIdentity(content, options);\n if (!options.withItemData && relativePath === \"context/PROTOTYPE.md\") {\n content = withoutItemGuidance(content);\n }\n files.push({ relativePath, content });\n }\n }\n }\n await visit(TEMPLATE_ROOT);\n return files;\n}\n\nfunction starterReadme(options: ConvertOptions) {\n return `# ${options.name}\\n\\nA Bonfire instance for creating, editing, and sharing prototypes.\\n\\n## Development\\n\\nUse Node.js 24+, install dependencies with pnpm, then run:\\n\\n~~~sh\\npnpm dev\\n~~~\\n\\nThe development command prepares the Bonfire session secret and the local identity branch before starting Next.js on port ${options.port}.\\n\\nRun pnpm context:install once on a machine where agent tooling is desired.\\n`;\n}\n\nfunction addDependency(\n packageJson: PackageJson,\n section: \"dependencies\" | \"devDependencies\",\n name: string,\n version: string,\n) {\n const target = (packageJson[section] ??= {});\n const existing = target[name];\n if (existing !== undefined) {\n if (existing !== version && name.startsWith(\"@ebay-dt/bonfire-\")) {\n throw new ConvertError(\n `${name} is already declared as ${existing}; expected ${version}. Update it explicitly before converting`,\n );\n }\n return;\n }\n target[name] = version;\n}\n\nfunction addScript(\n packageJson: PackageJson,\n name: string,\n value: string,\n allowedExisting: readonly string[] = [],\n) {\n const scripts = (packageJson.scripts ??= {});\n const existing = scripts[name];\n if (existing !== undefined) {\n if (existing !== value && !allowedExisting.includes(String(existing))) {\n throw new ConvertError(\n `package.json script ${name} is customized (${String(existing)}); resolve it before converting`,\n );\n }\n scripts[name] = value;\n return;\n }\n scripts[name] = value;\n}\n\nfunction mergeLines(existing: string | undefined, required: readonly string[]) {\n let result = existing ?? \"\";\n for (const line of required) {\n if (result.split(/\\r?\\n/u).some((entry) => entry.trim() === line)) continue;\n if (result.length > 0 && !result.endsWith(\"\\n\")) result += \"\\n\";\n result += `${line}\\n`;\n }\n return result;\n}\n\nfunction mergeWorkspace(existing: string | undefined) {\n let result = existing ?? \"\";\n if (!/^minimumReleaseAge:/mu.test(result)) {\n result = `# Bonfire publishes lockstep alpha packages that must be installable immediately.\\nminimumReleaseAge: 0\\n${result}`;\n } else if (!/^minimumReleaseAge:\\s*0\\s*$/mu.test(result)) {\n throw new ConvertError(\n \"pnpm-workspace.yaml has a conflicting minimumReleaseAge\",\n );\n }\n const required = [\n \" esbuild: true\",\n \" sharp: true\",\n \" unrs-resolver: true\",\n ];\n for (const line of required) {\n const key = line.trim().split(\":\")[0] ?? \"\";\n const match = result.match(new RegExp(`^\\\\s+${key}:\\\\s*(\\\\S+)\\\\s*$`, \"mu\"));\n if (match && match[1] !== \"true\") {\n // create-next-app currently writes these two false entries. They are\n // starter policy, not an instance decision, so normalize them to the\n // Bonfire installation policy.\n if (match[1] !== \"false\" || ![\"sharp\", \"unrs-resolver\"].includes(key)) {\n throw new ConvertError(\n `pnpm-workspace.yaml has a conflicting allowBuilds.${key} setting`,\n );\n }\n result = result.replace(\n new RegExp(`(^\\\\s+${key}:\\\\s*)false(\\\\s*$)`, \"mu\"),\n \"$1true$2\",\n );\n }\n if (!match) {\n if (!result.endsWith(\"\\n\")) result += \"\\n\";\n result += `${line}\\n`;\n }\n }\n return result;\n}\n\nasync function planPackageJson(\n root: string,\n options: ConvertOptions,\n packageJson: PackageJson,\n nextVersion: { major: number; minor: number; patch: number },\n writes: Map<string, string>,\n changes: PlannedChange[],\n) {\n const packageName =\n options.packageName ??\n (typeof packageJson.name === \"string\" ? packageJson.name : options.appId);\n if (!/^[a-z0-9][a-z0-9._-]*$/u.test(packageName)) {\n throw new ConvertError(`package name is invalid: ${packageName}`);\n }\n packageJson.name = packageName;\n const engines = (packageJson.engines ??= {});\n if (\n engines.node !== undefined &&\n !/^(?:\\^|~|>=)?\\s*2[4-9]|[3-9]\\d/u.test(engines.node)\n ) {\n throw new ConvertError(\n \"package.json engines.node must allow Node.js 24 or newer\",\n );\n }\n engines.node = \">=24\";\n packageJson.packageManager ??= \"pnpm@11.25.0\";\n\n const dev = `bonfire prepare --root . && bonfire branch prepare --root . --hostname ${options.hostname} && next dev -p ${options.port}`;\n addScript(packageJson, \"dev\", dev, [\"next dev\"]);\n addScript(packageJson, \"context:sync\", \"bonfire context sync\");\n addScript(\n packageJson,\n \"context:install\",\n `bonfire context install --app-id ${options.appId}`,\n );\n addScript(\n packageJson,\n \"check\",\n \"tsc --noEmit --project tsconfig.check.json && pnpm context:sync --check\",\n );\n addScript(packageJson, \"format\", \"prettier --write .\");\n addScript(packageJson, \"build\", \"next build\", [\"next build\"]);\n addScript(packageJson, \"start\", \"next start\", [\"next start\"]);\n addScript(packageJson, \"lint\", \"eslint\", [\"eslint\", \"eslint .\"]);\n\n const nextExact = `${nextVersion.major}.${nextVersion.minor}.${nextVersion.patch}`;\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-context\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-core\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-next\",\n BONFIRE_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-preset-ebay\",\n PRESET_VERSION,\n );\n addDependency(\n packageJson,\n \"dependencies\",\n \"@ebay-dt/bonfire-react\",\n BONFIRE_VERSION,\n );\n addDependency(packageJson, \"dependencies\", \"@ebay/ui-core-react\", \"9.13.0\");\n addDependency(packageJson, \"dependencies\", \"@evo-web/react\", \"0.4.4\");\n addDependency(\n packageJson,\n \"dependencies\",\n \"react-dom\",\n dependencyVersion(packageJson, \"react-dom\") ?? \"19.2.8\",\n );\n addDependency(packageJson, \"dependencies\", \"server-only\", \"^0.0.1\");\n addDependency(\n packageJson,\n \"devDependencies\",\n \"@ebay-dt/bonfire-cli\",\n BONFIRE_VERSION,\n );\n addDependency(packageJson, \"devDependencies\", \"@types/node\", \"^20\");\n addDependency(packageJson, \"devDependencies\", \"@types/react\", \"^19\");\n addDependency(packageJson, \"devDependencies\", \"@types/react-dom\", \"^19\");\n addDependency(packageJson, \"devDependencies\", \"eslint\", \"^9\");\n addDependency(\n packageJson,\n \"devDependencies\",\n \"eslint-config-next\",\n nextExact,\n );\n addDependency(packageJson, \"devDependencies\", \"prettier\", \"^3.9.5\");\n if (options.withItemData) {\n addDependency(\n packageJson,\n \"dependencies\",\n \"@tanstack/react-query\",\n \"^5.104.0\",\n );\n addDependency(packageJson, \"dependencies\", \"zod\", \"^4.4.3\");\n }\n\n const path = join(root, \"package.json\");\n const content = `${JSON.stringify(packageJson, null, 2)}\\n`;\n const existing = await readFile(path, \"utf8\");\n if (JSON.stringify(JSON.parse(existing)) !== JSON.stringify(packageJson)) {\n writes.set(path, content);\n addChange(\n changes,\n root,\n path,\n \"update\",\n \"merge Bonfire scripts and dependencies\",\n );\n }\n}\n\nexport async function planConversion(\n options: ConvertOptions,\n): Promise<ConversionPlan> {\n const root = resolve(options.root);\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(options.appId)) {\n throw new ConvertError(\n \"app id must contain only letters, numbers, dots, underscores, and hyphens\",\n );\n }\n if (!options.name.trim())\n throw new ConvertError(\"display name must not be empty\");\n const packagePath = join(root, \"package.json\");\n const packageJson = await readJson(packagePath);\n const nextVersion = validateStartingScaffold(packageJson);\n if (!(await exists(join(root, \"src\", \"app\")))) {\n throw new ConvertError(\n \"the starting scaffold must use the src/app App Router\",\n );\n }\n\n const writes = new Map<string, string>();\n const changes: PlannedChange[] = [];\n const removals: string[] = [];\n\n const starterPaths = [\n join(root, \"src/app/layout.tsx\"),\n join(root, \"src/app/page.tsx\"),\n join(root, \"src/app/page.module.css\"),\n join(root, \"src/app/globals.css\"),\n join(root, \"next.config.ts\"),\n join(root, \"README.md\"),\n ...[\"file.svg\", \"globe.svg\", \"next.svg\", \"vercel.svg\", \"window.svg\"].map(\n (name) => join(root, \"public\", name),\n ),\n ];\n for (const path of starterPaths) {\n await planRemoveStarter(\n root,\n path,\n \"remove unmodified create-next-app content\",\n removals,\n changes,\n );\n }\n const nextBuildDirectory = join(root, \".next\");\n if (await exists(nextBuildDirectory)) {\n removals.push(nextBuildDirectory);\n addChange(\n changes,\n root,\n nextBuildDirectory,\n \"remove\",\n \"clear generated Next.js output\",\n );\n }\n\n const resolvedOptions = { ...options, root };\n const templates = await templateFiles(resolvedOptions);\n for (const file of templates) {\n const path = join(root, file.relativePath);\n if (removals.includes(path)) {\n writes.set(path, file.content);\n addChange(\n changes,\n root,\n path,\n \"update\",\n \"replace unmodified create-next-app content\",\n );\n continue;\n }\n await planFile(\n root,\n path,\n file.content,\n \"add Bonfire instance scaffold\",\n writes,\n changes,\n );\n }\n\n await planPackageJson(\n root,\n options,\n packageJson,\n nextVersion,\n writes,\n changes,\n );\n\n const tsconfigPath = join(root, \"tsconfig.json\");\n const existingTsconfig = await readFile(tsconfigPath, \"utf8\");\n const tsconfig = await readJson(tsconfigPath);\n const compilerOptions = (tsconfig.compilerOptions ?? {}) as Record<\n string,\n unknown\n >;\n tsconfig.compilerOptions = compilerOptions;\n const paths = compilerOptions[\"paths\"] as Record<string, unknown> | undefined;\n if (paths && JSON.stringify(paths[\"@/*\"]) !== JSON.stringify([\"./src/*\"])) {\n throw new ConvertError(\"tsconfig.json has a conflicting @/* path alias\");\n }\n compilerOptions[\"paths\"] = { ...(paths ?? {}), \"@/*\": [\"./src/*\"] };\n const nextTsconfig = `${JSON.stringify(tsconfig, null, 2)}\\n`;\n if (\n JSON.stringify(JSON.parse(existingTsconfig)) !== JSON.stringify(tsconfig)\n ) {\n writes.set(tsconfigPath, nextTsconfig);\n addChange(\n changes,\n root,\n tsconfigPath,\n \"update\",\n \"retain the Bonfire @/* source alias\",\n );\n }\n\n await planFile(\n root,\n join(root, \"tsconfig.check.json\"),\n '{\\n \"extends\": \"./tsconfig.json\",\\n \"exclude\": [\"node_modules\", \".next\"]\\n}\\n',\n \"add the clean TypeScript check project\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".prettierrc\"),\n '{\\n \"semi\": false,\\n \"singleQuote\": true,\\n \"trailingComma\": \"es5\",\\n \"printWidth\": 80\\n}\\n',\n \"add Bonfire formatting conventions\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".prettierignore\"),\n \"# Generated and external files\\n.next/\\nnode_modules/\\npnpm-lock.yaml\\nsrc/app/(prototypes)/**/.claude/settings.json\\n\",\n \"add formatter exclusions\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \".env.local.example\"),\n \"# Bonfire generates this secret during `pnpm dev`.\\n# Do NOT manually modify or remove this variable.\\nBF_SESSION_SECRET=\\n\\n# Set exactly to lowercase `true` to skip automatic identity-branch preparation.\\nBF_DISABLE_AUTO_BRANCH_SWITCH=\\n\",\n \"document Bonfire environment setup\",\n writes,\n changes,\n );\n await planFile(\n root,\n join(root, \"public/.gitkeep\"),\n \"\",\n \"retain an explicit public directory\",\n writes,\n changes,\n );\n\n const gitignorePath = join(root, \".gitignore\");\n const gitignore = await fileContent(gitignorePath);\n const mergedGitignore = mergeLines(gitignore, [\n \"# Bonfire local secrets\",\n \".env.local\",\n ]);\n if (mergedGitignore !== (gitignore ?? \"\")) {\n writes.set(gitignorePath, mergedGitignore);\n addChange(\n changes,\n root,\n gitignorePath,\n \"update\",\n \"ignore the generated local session secret\",\n );\n }\n\n const workspacePath = join(root, \"pnpm-workspace.yaml\");\n const workspace = await fileContent(workspacePath);\n const mergedWorkspace = mergeWorkspace(workspace);\n if (mergedWorkspace !== (workspace ?? \"\")) {\n writes.set(workspacePath, mergedWorkspace);\n addChange(\n changes,\n root,\n workspacePath,\n \"update\",\n \"retain the Bonfire pnpm installation policy\",\n );\n }\n const npmrcPath = join(root, \".npmrc\");\n const npmrc = await fileContent(npmrcPath);\n if (\n npmrc &&\n /@ebay-dt:\\s*registry=/u.test(npmrc) &&\n !npmrc.includes(\"@ebay-dt:registry=https://registry.npmjs.org/\")\n ) {\n throw new ConvertError(\".npmrc has a conflicting @ebay-dt registry\");\n }\n const mergedNpmrc = mergeLines(npmrc, [\n \"@ebay-dt:registry=https://registry.npmjs.org/\",\n ]);\n if (mergedNpmrc !== (npmrc ?? \"\")) {\n writes.set(npmrcPath, mergedNpmrc);\n addChange(\n changes,\n root,\n npmrcPath,\n \"update\",\n \"resolve Bonfire packages from public npm\",\n );\n }\n const nvmrcPath = join(root, \".nvmrc\");\n const nvmrc = await fileContent(nvmrcPath);\n if (nvmrc === undefined) {\n await planFile(\n root,\n nvmrcPath,\n \"24\\n\",\n \"select Node.js 24 or newer\",\n writes,\n changes,\n );\n }\n\n const readmePath = join(root, \"README.md\");\n if (removals.includes(readmePath)) {\n writes.set(readmePath, starterReadme(options));\n addChange(\n changes,\n root,\n readmePath,\n \"update\",\n \"replace unmodified create-next-app documentation\",\n );\n } else if (\n !writes.has(readmePath) &&\n (await fileContent(readmePath)) === undefined\n ) {\n await planFile(\n root,\n readmePath,\n starterReadme(options),\n \"add instance setup documentation\",\n writes,\n changes,\n );\n }\n\n return { root, changes, writes, removals, packageJson };\n}\n\nexport async function convert(options: ConvertOptions) {\n const plan = await planConversion(options);\n for (const change of plan.changes) {\n console.log(\n `${options.dryRun ? \"Would \" : \"\"}${change.kind} ${change.path} (${change.reason})`,\n );\n }\n if (options.dryRun) return;\n\n for (const path of plan.removals) {\n await rm(path, {\n force: true,\n recursive: path === join(plan.root, \".next\"),\n });\n }\n for (const [path, content] of plan.writes) {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content);\n if (path.endsWith(\"context/hooks/block-env-reads.mjs\")) {\n await chmod(path, 0o755);\n }\n }\n\n if (!options.skipInstall) {\n console.log(\"Installing dependencies and updating pnpm-lock.yaml...\");\n await execFileAsync(\"pnpm\", [\"install\"], {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n });\n }\n\n if (!options.skipInstall) {\n const formatPaths = [...plan.writes.keys()]\n .map((path) => displayPath(plan.root, path))\n .filter((path) => /\\.(?:json|mjs|md|ts|tsx|yaml|yml)$/u.test(path));\n if (formatPaths.length > 0) {\n await execFileAsync(\n \"pnpm\",\n [\"exec\", \"prettier\", \"--write\", ...formatPaths],\n {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n },\n );\n }\n await execFileAsync(\"pnpm\", [\"exec\", \"next\", \"typegen\"], {\n cwd: plan.root,\n maxBuffer: 10 * 1024 * 1024,\n });\n }\n\n const contextResult = await syncContext({ repositoryRoot: plan.root });\n if (contextResult.written.length > 0 || contextResult.removed.length > 0) {\n console.log(\n `Context synchronized (${contextResult.written.length} written, ${contextResult.removed.length} removed).`,\n );\n }\n console.log(\"Bonfire instance conversion complete.\");\n}\n\nexport function defaultConvertOptions(root = process.cwd()): ConvertOptions {\n const appId = \"bonfire-instance\";\n return {\n root,\n appId,\n name: \"Bonfire Instance\",\n hostname: DEFAULT_HOSTNAME,\n port: DEFAULT_PORT,\n deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,\n withItemData: true,\n dryRun: false,\n skipInstall: false,\n };\n}\n\nexport const convertDefaults = {\n hostname: DEFAULT_HOSTNAME,\n deploymentEndpoint: DEFAULT_DEPLOYMENT_ENDPOINT,\n port: DEFAULT_PORT,\n};\n"]}
|