@dbx-tools/core 0.3.43 → 0.4.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/lib/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * as brand from "./src/brand.js";
2
+ export * as exec from "./src/exec.js";
3
+ export * as file from "./src/file.js";
4
+ export * as project from "./src/project.js";
5
+ export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt, findBrandContextFile, loadBrandContextFile, loadBrandContext, resolveBrandAssetPath } from "./src/brand.js";
6
+ export type { BrandContext, BrandContextInput } from "./src/brand.js";
7
+ export { spawn, spawnSync, shlex } from "./src/exec.js";
8
+ export type { ExecStdio, LineHandler, StdioOption, ExecResult, ExecOptions, SyncExecStdio, SyncExecOptions, SpawnArgs } from "./src/exec.js";
9
+ export { statSync } from "./src/file.js";
10
+ export { root, parseGitRemote, resolveProjectRoots, name, repositoryUrl, npmRegistry } from "./src/project.js";
11
+ export type { ProjectContext } from "./src/project.js";
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+ export * as brand from "./src/brand.js";
5
+ export * as exec from "./src/exec.js";
6
+ export * as file from "./src/file.js";
7
+ export * as project from "./src/project.js";
8
+ export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt, findBrandContextFile, loadBrandContextFile, loadBrandContext, resolveBrandAssetPath } from "./src/brand.js";
9
+ export { spawn, spawnSync, shlex } from "./src/exec.js";
10
+ export { statSync } from "./src/file.js";
11
+ export { root, parseGitRemote, resolveProjectRoots, name, repositoryUrl, npmRegistry } from "./src/project.js";
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEtBQUssSUFBSSxNQUFNLFlBQVksQ0FBQztBQUNuQyxPQUFPLEtBQUssSUFBSSxNQUFNLFlBQVksQ0FBQztBQUNuQyxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsc0JBQXNCLEVBQUUsa0JBQWtCLEVBQUUsb0JBQW9CLEVBQUUsb0JBQW9CLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFMU4sT0FBTyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXJELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDdEMsT0FBTyxFQUFFLElBQUksRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBHRU5FUkFURUQgYnkgcHJvamVuIHdhdGNoIC0gRE8gTk9UIEVESVQuXG4vLyBSZWdlbmVyYXRlZCBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlcyBpbiAuL3NyYy5cbi8vIEhhbmQgZWRpdHMgYXJlIG92ZXJ3cml0dGVuIG9uIHRoZSBuZXh0IHdhdGNoOyB0aGlzIGZpbGUgaXMgcmVhZC1vbmx5LlxuXG5leHBvcnQgKiBhcyBicmFuZCBmcm9tIFwiLi9zcmMvYnJhbmRcIjtcbmV4cG9ydCAqIGFzIGV4ZWMgZnJvbSBcIi4vc3JjL2V4ZWNcIjtcbmV4cG9ydCAqIGFzIGZpbGUgZnJvbSBcIi4vc3JjL2ZpbGVcIjtcbmV4cG9ydCAqIGFzIHByb2plY3QgZnJvbSBcIi4vc3JjL3Byb2plY3RcIjtcbmV4cG9ydCB7IEJyYW5kQ29udGV4dFNjaGVtYSwgZGVmYXVsdEJyYW5kQ29udGV4dCwgcGFyc2VCcmFuZENvbnRleHQsIGJyYW5kQ29udGV4dEpzb25TY2hlbWEsIGJyYW5kQ29udGV4dFByb21wdCwgZmluZEJyYW5kQ29udGV4dEZpbGUsIGxvYWRCcmFuZENvbnRleHRGaWxlLCBsb2FkQnJhbmRDb250ZXh0LCByZXNvbHZlQnJhbmRBc3NldFBhdGggfSBmcm9tIFwiLi9zcmMvYnJhbmRcIjtcbmV4cG9ydCB0eXBlIHsgQnJhbmRDb250ZXh0LCBCcmFuZENvbnRleHRJbnB1dCB9IGZyb20gXCIuL3NyYy9icmFuZFwiO1xuZXhwb3J0IHsgc3Bhd24sIHNwYXduU3luYywgc2hsZXggfSBmcm9tIFwiLi9zcmMvZXhlY1wiO1xuZXhwb3J0IHR5cGUgeyBFeGVjU3RkaW8sIExpbmVIYW5kbGVyLCBTdGRpb09wdGlvbiwgRXhlY1Jlc3VsdCwgRXhlY09wdGlvbnMsIFN5bmNFeGVjU3RkaW8sIFN5bmNFeGVjT3B0aW9ucywgU3Bhd25BcmdzIH0gZnJvbSBcIi4vc3JjL2V4ZWNcIjtcbmV4cG9ydCB7IHN0YXRTeW5jIH0gZnJvbSBcIi4vc3JjL2ZpbGVcIjtcbmV4cG9ydCB7IHJvb3QsIHBhcnNlR2l0UmVtb3RlLCByZXNvbHZlUHJvamVjdFJvb3RzLCBuYW1lLCByZXBvc2l0b3J5VXJsLCBucG1SZWdpc3RyeSB9IGZyb20gXCIuL3NyYy9wcm9qZWN0XCI7XG5leHBvcnQgdHlwZSB7IFByb2plY3RDb250ZXh0IH0gZnJvbSBcIi4vc3JjL3Byb2plY3RcIjtcbiJdfQ==
@@ -0,0 +1,105 @@
1
+ import { brand as sharedBrand } from "@dbx-tools/shared-core";
2
+ export type BrandContext = sharedBrand.BrandContext;
3
+ export type BrandContextInput = sharedBrand.BrandContextInput;
4
+ export declare const BrandContextSchema: import("zod").ZodObject<{
5
+ schemaVersion: import("zod").ZodDefault<import("zod").ZodLiteral<"1">>;
6
+ name: import("zod").ZodDefault<import("zod").ZodString>;
7
+ shortName: import("zod").ZodDefault<import("zod").ZodString>;
8
+ tagline: import("zod").ZodDefault<import("zod").ZodString>;
9
+ description: import("zod").ZodDefault<import("zod").ZodString>;
10
+ assets: import("zod").ZodDefault<import("zod").ZodObject<{
11
+ icon: import("zod").ZodDefault<import("zod").ZodObject<{
12
+ light: import("zod").ZodString;
13
+ dark: import("zod").ZodOptional<import("zod").ZodString>;
14
+ }, import("zod/v4/core").$strict>>;
15
+ logo: import("zod").ZodDefault<import("zod").ZodObject<{
16
+ light: import("zod").ZodString;
17
+ dark: import("zod").ZodOptional<import("zod").ZodString>;
18
+ }, import("zod/v4/core").$strict>>;
19
+ favicon: import("zod").ZodDefault<import("zod").ZodString>;
20
+ }, import("zod/v4/core").$strict>>;
21
+ colors: import("zod").ZodPrefault<import("zod").ZodObject<{
22
+ primary: import("zod").ZodDefault<import("zod").ZodString>;
23
+ primaryHover: import("zod").ZodDefault<import("zod").ZodString>;
24
+ accent: import("zod").ZodDefault<import("zod").ZodString>;
25
+ foreground: import("zod").ZodDefault<import("zod").ZodString>;
26
+ background: import("zod").ZodDefault<import("zod").ZodString>;
27
+ surface: import("zod").ZodDefault<import("zod").ZodString>;
28
+ muted: import("zod").ZodDefault<import("zod").ZodString>;
29
+ border: import("zod").ZodDefault<import("zod").ZodString>;
30
+ }, import("zod/v4/core").$strict>>;
31
+ typography: import("zod").ZodPrefault<import("zod").ZodObject<{
32
+ sans: import("zod").ZodDefault<import("zod").ZodString>;
33
+ mono: import("zod").ZodDefault<import("zod").ZodString>;
34
+ }, import("zod/v4/core").$strict>>;
35
+ voice: import("zod").ZodPrefault<import("zod").ZodObject<{
36
+ audience: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
37
+ tone: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
38
+ principles: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
39
+ avoid: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
40
+ }, import("zod/v4/core").$strict>>;
41
+ links: import("zod").ZodDefault<import("zod").ZodObject<{
42
+ website: import("zod").ZodOptional<import("zod").ZodString>;
43
+ repository: import("zod").ZodOptional<import("zod").ZodString>;
44
+ documentation: import("zod").ZodOptional<import("zod").ZodString>;
45
+ }, import("zod/v4/core").$strict>>;
46
+ extensions: import("zod").ZodDefault<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
47
+ }, import("zod/v4/core").$strict>;
48
+ export declare const defaultBrandContext: {
49
+ schemaVersion: "1";
50
+ name: string;
51
+ shortName: string;
52
+ tagline: string;
53
+ description: string;
54
+ assets: {
55
+ icon: {
56
+ light: string;
57
+ dark?: string | undefined;
58
+ };
59
+ logo: {
60
+ light: string;
61
+ dark?: string | undefined;
62
+ };
63
+ favicon: string;
64
+ };
65
+ colors: {
66
+ primary: string;
67
+ primaryHover: string;
68
+ accent: string;
69
+ foreground: string;
70
+ background: string;
71
+ surface: string;
72
+ muted: string;
73
+ border: string;
74
+ };
75
+ typography: {
76
+ sans: string;
77
+ mono: string;
78
+ };
79
+ voice: {
80
+ audience: string[];
81
+ tone: string[];
82
+ principles: string[];
83
+ avoid: string[];
84
+ };
85
+ links: {
86
+ website?: string | undefined;
87
+ repository?: string | undefined;
88
+ documentation?: string | undefined;
89
+ };
90
+ extensions: Record<string, unknown>;
91
+ };
92
+ export declare const parseBrandContext: typeof sharedBrand.parseBrandContext;
93
+ export declare const brandContextJsonSchema: typeof sharedBrand.brandContextJsonSchema;
94
+ export declare const brandContextPrompt: typeof sharedBrand.brandContextPrompt;
95
+ /** Find a conventional YAML or JSON brand file from known project roots. */
96
+ export declare function findBrandContextFile(cwd?: string): string | undefined;
97
+ /** Read and validate one `.yaml`, `.yml`, or `.json` brand context file. */
98
+ export declare function loadBrandContextFile(path: string): Promise<BrandContext>;
99
+ /**
100
+ * Discover and load a brand context. Missing files resolve to dbx tools defaults;
101
+ * malformed files fail with their parser or Zod validation error.
102
+ */
103
+ export declare function loadBrandContext(cwd?: string): Promise<BrandContext>;
104
+ /** Resolve a relative asset reference against the brand file that declared it. */
105
+ export declare function resolveBrandAssetPath(brandFile: string, asset: string): string;
@@ -0,0 +1,66 @@
1
+ /** Node-only discovery and file loading for the shared brand context. */
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, extname, isAbsolute, resolve } from "node:path";
4
+ import { brand as sharedBrand } from "@dbx-tools/shared-core";
5
+ import { statSync } from "./file.js";
6
+ import { resolveProjectRoots } from "./project.js";
7
+ const BRAND_CONTEXT_FILES = [
8
+ "branding/brand.yaml",
9
+ "branding/brand.yml",
10
+ "branding/brand.json",
11
+ "brand.yaml",
12
+ "brand.yml",
13
+ "brand.json",
14
+ ];
15
+ export const BrandContextSchema = sharedBrand.BrandContextSchema;
16
+ export const defaultBrandContext = sharedBrand.defaultBrandContext;
17
+ export const parseBrandContext = sharedBrand.parseBrandContext;
18
+ export const brandContextJsonSchema = sharedBrand.brandContextJsonSchema;
19
+ export const brandContextPrompt = sharedBrand.brandContextPrompt;
20
+ /** Find a conventional YAML or JSON brand file from known project roots. */
21
+ export function findBrandContextFile(cwd = process.cwd()) {
22
+ for (const root of resolveProjectRoots(cwd)) {
23
+ for (const candidate of BRAND_CONTEXT_FILES) {
24
+ const path = resolve(root, candidate);
25
+ if (statSync(path)?.isFile())
26
+ return path;
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ /** Read and validate one `.yaml`, `.yml`, or `.json` brand context file. */
32
+ export async function loadBrandContextFile(path) {
33
+ const source = await readFile(path, "utf8");
34
+ const extension = extname(path).toLowerCase();
35
+ let input;
36
+ if (extension === ".json") {
37
+ input = JSON.parse(source);
38
+ }
39
+ else if (extension === ".yaml" || extension === ".yml") {
40
+ const { parse } = await import("yaml");
41
+ input = parse(source);
42
+ }
43
+ else {
44
+ throw new Error(`Unsupported brand context format: ${extension || "no extension"}`);
45
+ }
46
+ return sharedBrand.parseBrandContext(input);
47
+ }
48
+ /**
49
+ * Discover and load a brand context. Missing files resolve to dbx tools defaults;
50
+ * malformed files fail with their parser or Zod validation error.
51
+ */
52
+ export async function loadBrandContext(cwd = process.cwd()) {
53
+ const path = findBrandContextFile(cwd);
54
+ return path ? loadBrandContextFile(path) : sharedBrand.defaultBrandContext;
55
+ }
56
+ /** Resolve a relative asset reference against the brand file that declared it. */
57
+ export function resolveBrandAssetPath(brandFile, asset) {
58
+ if (isAbsolute(asset) ||
59
+ asset.startsWith("@") ||
60
+ asset.startsWith("//") ||
61
+ /^[a-z][a-z\d+.-]*:/i.test(asset)) {
62
+ return asset;
63
+ }
64
+ return resolve(dirname(brandFile), asset);
65
+ }
66
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJhbmQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYnJhbmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseUVBQXlFO0FBQ3pFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxLQUFLLElBQUksV0FBVyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNsQyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFaEQsTUFBTSxtQkFBbUIsR0FBRztJQUMxQixxQkFBcUI7SUFDckIsb0JBQW9CO0lBQ3BCLHFCQUFxQjtJQUNyQixZQUFZO0lBQ1osV0FBVztJQUNYLFlBQVk7Q0FDSixDQUFDO0FBSVgsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsV0FBVyxDQUFDLGtCQUFrQixDQUFDO0FBQ2pFLE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUFHLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQztBQUNuRSxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxXQUFXLENBQUMsaUJBQWlCLENBQUM7QUFDL0QsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsV0FBVyxDQUFDLHNCQUFzQixDQUFDO0FBQ3pFLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLFdBQVcsQ0FBQyxrQkFBa0IsQ0FBQztBQUVqRSw0RUFBNEU7QUFDNUUsTUFBTSxVQUFVLG9CQUFvQixDQUFDLE1BQWMsT0FBTyxDQUFDLEdBQUcsRUFBRTtJQUM5RCxLQUFLLE1BQU0sSUFBSSxJQUFJLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDNUMsS0FBSyxNQUFNLFNBQVMsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO1lBQzVDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDdEMsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFO2dCQUFFLE9BQU8sSUFBSSxDQUFDO1FBQzVDLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVELDRFQUE0RTtBQUM1RSxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUFDLElBQVk7SUFDckQsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzVDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUM5QyxJQUFJLEtBQWMsQ0FBQztJQUVuQixJQUFJLFNBQVMsS0FBSyxPQUFPLEVBQUUsQ0FBQztRQUMxQixLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQVksQ0FBQztJQUN4QyxDQUFDO1NBQU0sSUFBSSxTQUFTLEtBQUssT0FBTyxJQUFJLFNBQVMsS0FBSyxNQUFNLEVBQUUsQ0FBQztRQUN6RCxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsTUFBTSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdkMsS0FBSyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQVksQ0FBQztJQUNuQyxDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sSUFBSSxLQUFLLENBQUMscUNBQXFDLFNBQVMsSUFBSSxjQUFjLEVBQUUsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFFRCxPQUFPLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUM5QyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxNQUFjLE9BQU8sQ0FBQyxHQUFHLEVBQUU7SUFDaEUsTUFBTSxJQUFJLEdBQUcsb0JBQW9CLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkMsT0FBTyxJQUFJLENBQUMsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsbUJBQW1CLENBQUM7QUFDN0UsQ0FBQztBQUVELGtGQUFrRjtBQUNsRixNQUFNLFVBQVUscUJBQXFCLENBQUMsU0FBaUIsRUFBRSxLQUFhO0lBQ3BFLElBQ0UsVUFBVSxDQUFDLEtBQUssQ0FBQztRQUNqQixLQUFLLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQztRQUNyQixLQUFLLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQztRQUN0QixxQkFBcUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQ2pDLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7QUFDNUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKiBOb2RlLW9ubHkgZGlzY292ZXJ5IGFuZCBmaWxlIGxvYWRpbmcgZm9yIHRoZSBzaGFyZWQgYnJhbmQgY29udGV4dC4gKi9cbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IGRpcm5hbWUsIGV4dG5hbWUsIGlzQWJzb2x1dGUsIHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBicmFuZCBhcyBzaGFyZWRCcmFuZCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBzdGF0U3luYyB9IGZyb20gXCIuL2ZpbGVcIjtcbmltcG9ydCB7IHJlc29sdmVQcm9qZWN0Um9vdHMgfSBmcm9tIFwiLi9wcm9qZWN0XCI7XG5cbmNvbnN0IEJSQU5EX0NPTlRFWFRfRklMRVMgPSBbXG4gIFwiYnJhbmRpbmcvYnJhbmQueWFtbFwiLFxuICBcImJyYW5kaW5nL2JyYW5kLnltbFwiLFxuICBcImJyYW5kaW5nL2JyYW5kLmpzb25cIixcbiAgXCJicmFuZC55YW1sXCIsXG4gIFwiYnJhbmQueW1sXCIsXG4gIFwiYnJhbmQuanNvblwiLFxuXSBhcyBjb25zdDtcblxuZXhwb3J0IHR5cGUgQnJhbmRDb250ZXh0ID0gc2hhcmVkQnJhbmQuQnJhbmRDb250ZXh0O1xuZXhwb3J0IHR5cGUgQnJhbmRDb250ZXh0SW5wdXQgPSBzaGFyZWRCcmFuZC5CcmFuZENvbnRleHRJbnB1dDtcbmV4cG9ydCBjb25zdCBCcmFuZENvbnRleHRTY2hlbWEgPSBzaGFyZWRCcmFuZC5CcmFuZENvbnRleHRTY2hlbWE7XG5leHBvcnQgY29uc3QgZGVmYXVsdEJyYW5kQ29udGV4dCA9IHNoYXJlZEJyYW5kLmRlZmF1bHRCcmFuZENvbnRleHQ7XG5leHBvcnQgY29uc3QgcGFyc2VCcmFuZENvbnRleHQgPSBzaGFyZWRCcmFuZC5wYXJzZUJyYW5kQ29udGV4dDtcbmV4cG9ydCBjb25zdCBicmFuZENvbnRleHRKc29uU2NoZW1hID0gc2hhcmVkQnJhbmQuYnJhbmRDb250ZXh0SnNvblNjaGVtYTtcbmV4cG9ydCBjb25zdCBicmFuZENvbnRleHRQcm9tcHQgPSBzaGFyZWRCcmFuZC5icmFuZENvbnRleHRQcm9tcHQ7XG5cbi8qKiBGaW5kIGEgY29udmVudGlvbmFsIFlBTUwgb3IgSlNPTiBicmFuZCBmaWxlIGZyb20ga25vd24gcHJvamVjdCByb290cy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kQnJhbmRDb250ZXh0RmlsZShjd2Q6IHN0cmluZyA9IHByb2Nlc3MuY3dkKCkpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBmb3IgKGNvbnN0IHJvb3Qgb2YgcmVzb2x2ZVByb2plY3RSb290cyhjd2QpKSB7XG4gICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2YgQlJBTkRfQ09OVEVYVF9GSUxFUykge1xuICAgICAgY29uc3QgcGF0aCA9IHJlc29sdmUocm9vdCwgY2FuZGlkYXRlKTtcbiAgICAgIGlmIChzdGF0U3luYyhwYXRoKT8uaXNGaWxlKCkpIHJldHVybiBwYXRoO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG4vKiogUmVhZCBhbmQgdmFsaWRhdGUgb25lIGAueWFtbGAsIGAueW1sYCwgb3IgYC5qc29uYCBicmFuZCBjb250ZXh0IGZpbGUuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZEJyYW5kQ29udGV4dEZpbGUocGF0aDogc3RyaW5nKTogUHJvbWlzZTxCcmFuZENvbnRleHQ+IHtcbiAgY29uc3Qgc291cmNlID0gYXdhaXQgcmVhZEZpbGUocGF0aCwgXCJ1dGY4XCIpO1xuICBjb25zdCBleHRlbnNpb24gPSBleHRuYW1lKHBhdGgpLnRvTG93ZXJDYXNlKCk7XG4gIGxldCBpbnB1dDogdW5rbm93bjtcblxuICBpZiAoZXh0ZW5zaW9uID09PSBcIi5qc29uXCIpIHtcbiAgICBpbnB1dCA9IEpTT04ucGFyc2Uoc291cmNlKSBhcyB1bmtub3duO1xuICB9IGVsc2UgaWYgKGV4dGVuc2lvbiA9PT0gXCIueWFtbFwiIHx8IGV4dGVuc2lvbiA9PT0gXCIueW1sXCIpIHtcbiAgICBjb25zdCB7IHBhcnNlIH0gPSBhd2FpdCBpbXBvcnQoXCJ5YW1sXCIpO1xuICAgIGlucHV0ID0gcGFyc2Uoc291cmNlKSBhcyB1bmtub3duO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgYnJhbmQgY29udGV4dCBmb3JtYXQ6ICR7ZXh0ZW5zaW9uIHx8IFwibm8gZXh0ZW5zaW9uXCJ9YCk7XG4gIH1cblxuICByZXR1cm4gc2hhcmVkQnJhbmQucGFyc2VCcmFuZENvbnRleHQoaW5wdXQpO1xufVxuXG4vKipcbiAqIERpc2NvdmVyIGFuZCBsb2FkIGEgYnJhbmQgY29udGV4dC4gTWlzc2luZyBmaWxlcyByZXNvbHZlIHRvIGRieCB0b29scyBkZWZhdWx0cztcbiAqIG1hbGZvcm1lZCBmaWxlcyBmYWlsIHdpdGggdGhlaXIgcGFyc2VyIG9yIFpvZCB2YWxpZGF0aW9uIGVycm9yLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZEJyYW5kQ29udGV4dChjd2Q6IHN0cmluZyA9IHByb2Nlc3MuY3dkKCkpOiBQcm9taXNlPEJyYW5kQ29udGV4dD4ge1xuICBjb25zdCBwYXRoID0gZmluZEJyYW5kQ29udGV4dEZpbGUoY3dkKTtcbiAgcmV0dXJuIHBhdGggPyBsb2FkQnJhbmRDb250ZXh0RmlsZShwYXRoKSA6IHNoYXJlZEJyYW5kLmRlZmF1bHRCcmFuZENvbnRleHQ7XG59XG5cbi8qKiBSZXNvbHZlIGEgcmVsYXRpdmUgYXNzZXQgcmVmZXJlbmNlIGFnYWluc3QgdGhlIGJyYW5kIGZpbGUgdGhhdCBkZWNsYXJlZCBpdC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlQnJhbmRBc3NldFBhdGgoYnJhbmRGaWxlOiBzdHJpbmcsIGFzc2V0OiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoXG4gICAgaXNBYnNvbHV0ZShhc3NldCkgfHxcbiAgICBhc3NldC5zdGFydHNXaXRoKFwiQFwiKSB8fFxuICAgIGFzc2V0LnN0YXJ0c1dpdGgoXCIvL1wiKSB8fFxuICAgIC9eW2Etel1bYS16XFxkKy4tXSo6L2kudGVzdChhc3NldClcbiAgKSB7XG4gICAgcmV0dXJuIGFzc2V0O1xuICB9XG4gIHJldHVybiByZXNvbHZlKGRpcm5hbWUoYnJhbmRGaWxlKSwgYXNzZXQpO1xufVxuIl19
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Portable subprocess helper built on `child_process.spawn` and line streaming.
3
+ *
4
+ * Ported from `dbx-tools-js/packages/cli/src/exec.ts`. Each stdio fd defaults to
5
+ * `"inherit"`. {@link spawn} streams output line-by-line into {@link ExecResult.stdoutLines}
6
+ * / {@link ExecResult.stderrLines}; its `stdout` / `stderr` getters join those lines.
7
+ * {@link spawnSync} keeps the captured string; its `stdout` / `stderr` getters read
8
+ * that string directly (line arrays split lazily on read).
9
+ * Omitted `trim` (default) applies adaptive normalization: {@link spawnSync} drops
10
+ * at most one trailing empty line / newline `spawnSync` adds; {@link spawn} does
11
+ * not (readline never emits that extra line). `trim: true` strips all leading/
12
+ * trailing whitespace in both modes; `trim: false` leaves output unchanged.
13
+ *
14
+ * @example Capture command output
15
+ * ```ts
16
+ * const { stdout } = await exec("git", ["rev-parse", "--show-toplevel"], {
17
+ * stdout: "capture",
18
+ * stderr: "ignore",
19
+ * stdin: "ignore",
20
+ * });
21
+ * ```
22
+ *
23
+ * @example Stream and capture together
24
+ * ```ts
25
+ * await exec("pnpm", ["install"], {
26
+ * stdout: [(line) => console.log(line), "capture"],
27
+ * check: true,
28
+ * });
29
+ * ```
30
+ *
31
+ * @example Synchronous capture (no line callbacks)
32
+ * ```ts
33
+ * const { stdout } = execSync("git", ["rev-parse", "--show-toplevel"], {
34
+ * stdout: "capture",
35
+ * stderr: "ignore",
36
+ * stdin: "ignore",
37
+ * });
38
+ * ```
39
+ *
40
+ * @module
41
+ */
42
+ import { type SpawnOptions } from "node:child_process";
43
+ /** Stdio mode for a subprocess fd. */
44
+ export type ExecStdio = "inherit" | "pipe" | "ignore";
45
+ /** Invoked once per output line when a fd is piped. */
46
+ export type LineHandler = (line: string) => void;
47
+ /**
48
+ * Stdio config for one fd.
49
+ *
50
+ * - `"inherit"` / `"pipe"` / `"ignore"` — pass through to `spawn`
51
+ * - `"capture"` — pipe the fd and append each line to the result
52
+ * - {@link LineHandler} — pipe and invoke the handler per line (lines are still captured)
53
+ * - `(LineHandler | "capture")[]` — pipe; `"capture"` is a no-op marker, handlers run per line
54
+ */
55
+ export type StdioOption = ExecStdio | LineHandler | "capture" | (LineHandler | "capture")[];
56
+ /** Outcome of {@link spawn} / {@link spawnSync}: exit code, captured output, and line views. */
57
+ export type ExecResult = {
58
+ exitCode: number;
59
+ /**
60
+ * Captured stdout lines. For {@link spawn} these are built while the process runs;
61
+ * for {@link spawnSync} they are split from the captured string on first read.
62
+ */
63
+ readonly stdoutLines: string[];
64
+ /**
65
+ * Captured stderr lines. For {@link spawn} these are built while the process runs;
66
+ * for {@link spawnSync} they are split from the captured string on first read.
67
+ */
68
+ readonly stderrLines: string[];
69
+ /**
70
+ * Captured stdout text. {@link spawnSync} reads the captured string;
71
+ * {@link spawn} joins {@link stdoutLines}. See `trim` for normalization.
72
+ */
73
+ readonly stdout: string;
74
+ /**
75
+ * Captured stderr text. {@link spawnSync} reads the captured string;
76
+ * {@link spawn} joins {@link stderrLines}. See `trim` for normalization.
77
+ */
78
+ readonly stderr: string;
79
+ };
80
+ /** Options for {@link spawn}. Extends `SpawnOptions` except `stdio`, which is driven by `stdin` / `stdout` / `stderr`. */
81
+ export type ExecOptions = Omit<SpawnOptions, "stdio"> & {
82
+ /** `"inherit"` by default, or a string written to the process stdin. */
83
+ stdin?: ExecStdio | string;
84
+ stdout?: StdioOption;
85
+ stderr?: StdioOption;
86
+ /** Throw when the process exits with a non-zero code. */
87
+ check?: boolean;
88
+ /**
89
+ * Omitted — adaptive trim ({@link spawnSync} drops one spawn trailing newline,
90
+ * {@link spawn} does not); `true` — strip all leading/trailing whitespace;
91
+ * `false` — leave captured output unchanged.
92
+ */
93
+ trim?: boolean;
94
+ };
95
+ /** Stdio mode for {@link spawnSync} (no per-line callbacks). */
96
+ export type SyncExecStdio = ExecStdio | "capture";
97
+ /** Options for {@link spawnSync}. Same shape as {@link ExecOptions} but without line-handler stdio. */
98
+ export type SyncExecOptions = Omit<SpawnOptions, "stdio"> & {
99
+ /** `"inherit"` by default, or a string written to the process stdin. */
100
+ stdin?: ExecStdio | string;
101
+ stdout?: SyncExecStdio;
102
+ stderr?: SyncExecStdio;
103
+ /** Throw when the process exits with a non-zero code. */
104
+ check?: boolean;
105
+ /**
106
+ * Omitted — adaptive trim ({@link spawnSync} drops one spawn trailing newline,
107
+ * {@link spawn} does not); `true` — strip all leading/trailing whitespace;
108
+ * `false` — leave captured output unchanged.
109
+ */
110
+ trim?: boolean;
111
+ };
112
+ export type SpawnArgs<T extends SpawnOptions> = [command: string, ...args: string[]] | [command: string, args: readonly string[]] | [command: string, args: readonly string[], options: T] | [command: string, ...argsAndOptions: [...string[], T]];
113
+ /**
114
+ * Spawn a subprocess and wait for exit.
115
+ *
116
+ * @param command - Executable to run (resolved on `PATH` when `shell` is set on options)
117
+ * @param args - Arguments passed verbatim to the executable
118
+ * @param options - Spawn, stdio, and check options
119
+ * @returns Exit code, captured line arrays, and trimmed `stdout` / `stderr` getters
120
+ * @throws When spawn fails, line reads fail, or `check` is true and exit code is non-zero
121
+ */
122
+ export declare function spawn(...args: SpawnArgs<ExecOptions>): Promise<ExecResult>;
123
+ /**
124
+ * Spawn a subprocess synchronously and wait for exit.
125
+ *
126
+ * Unlike {@link spawn}, stdio options are limited to `"inherit"`, `"pipe"`,
127
+ * `"ignore"`, and `"capture"` — no per-line callbacks.
128
+ *
129
+ * @param command - Executable to run (resolved on `PATH` when `shell` is set on options)
130
+ * @param args - Arguments passed verbatim to the executable
131
+ * @param spawnSync - Spawn, stdio, and check options
132
+ * @returns Exit code, captured line arrays, and trimmed `stdout` / `stderr` getters
133
+ * @throws When spawn fails or `check` is true and exit code is non-zero
134
+ */
135
+ export declare function spawnSync(...args: SpawnArgs<SyncExecOptions>): ExecResult;
136
+ /**
137
+ * Splits a shell-like command into argv.
138
+ *
139
+ * Supports:
140
+ * - whitespace separators
141
+ * - single and double quotes
142
+ * - backslash escaping
143
+ * - escaped spaces
144
+ * - empty quoted strings
145
+ *
146
+ * If the input is malformed (for example, an unterminated quote),
147
+ * returns the original string as a single argument.
148
+ */
149
+ export declare function shlex(command: string): string[];