@plexus-ms/lib 0.0.0-stage → 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.
@@ -0,0 +1,41 @@
1
+ import { type DirEntry, type ExecOptions, type Io } from './io.ts';
2
+ import { type Plan } from './plan.ts';
3
+ export interface Options {
4
+ dryRun?: boolean;
5
+ /** 0: every effect; 1: reads and inputs too; 2: subprocess output too. */
6
+ verbose?: number;
7
+ quiet?: boolean;
8
+ yes?: boolean;
9
+ json?: boolean;
10
+ }
11
+ export declare class Context {
12
+ readonly dryRun: boolean;
13
+ readonly verbose: number;
14
+ readonly quiet: boolean;
15
+ readonly yes: boolean;
16
+ readonly json: boolean;
17
+ readonly io: Io;
18
+ constructor(options: Options, io?: Io);
19
+ /** One line, if the verbosity allows the level (0 normal, 1 verbose, 2 debug). */
20
+ say(line: string, level?: number): void;
21
+ /** A final outcome line, `✓ ...` or `✗ ...`; shown even under --quiet. */
22
+ done(line: string, ok?: boolean): void;
23
+ /**
24
+ * The command's product. `data` is what --json prints; `human` renders it
25
+ * otherwise (default: strings as they are, arrays one item per line).
26
+ */
27
+ result<T>(data: T, human?: (data: T) => string): void;
28
+ /** Run a command for its output. Changes nothing; narrated only when verbose. */
29
+ read(command: string, args?: string[], options?: Omit<ExecOptions, 'inherit'>): Promise<string>;
30
+ readFile(path: string): Promise<string>;
31
+ readDir(path: string): Promise<DirEntry[]>;
32
+ /**
33
+ * Ask before applying. `--yes` and `--dry-run` answer yes; off a terminal
34
+ * without `--yes` the answer is an error, never a hang.
35
+ */
36
+ confirm(question: string): Promise<boolean>;
37
+ /** Narrate and perform each step in order; the first failure stops the run. */
38
+ apply(plan: Plan): Promise<void>;
39
+ private perform;
40
+ }
41
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,EAAU,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAkB,KAAK,IAAI,EAAa,MAAM,WAAW,CAAC;AAEjE,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,OAAO;IAClB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;gBAEJ,OAAO,EAAE,OAAO,EAAE,EAAE,GAAE,EAAa;IAW/C,kFAAkF;IAClF,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,IAAI;IAIlC,0EAA0E;IAC1E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAO,GAAG,IAAI;IAMnC;;;OAGG;IACH,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAoB,GAAG,IAAI;IAOlE,iFAAiF;IACjF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAO1C;;;OAGG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjD,+EAA+E;IACzE,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAStC,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,115 @@
1
+ // The Context is what a verb runs in: the options, the injected Io, and the
2
+ // three things a verb does with them.
3
+ //
4
+ // read look at the world; always performed, so a dry run sees real state
5
+ // apply perform a Plan (plan.ts); every step is narrated with its exact
6
+ // shell line, and performed unless --dry-run
7
+ // result print the command's product: human-readable, or JSON under --json
8
+ //
9
+ // Plan, then confirm, then apply: validation and every read happen while
10
+ // planning, so a failure there costs nothing, and by the time an effect runs
11
+ // the verb knows the whole of what it is about to do. Narration goes to
12
+ // stderr, results to stdout, so `plx <verb> | ...` composes like the script
13
+ // it replaces. Conventions: compendium/miscellaneous/cli-design.md.
14
+ import { realIo } from "./io.js";
15
+ import { body, describe } from "./plan.js";
16
+ export class Context {
17
+ dryRun;
18
+ verbose;
19
+ quiet;
20
+ yes;
21
+ json;
22
+ io;
23
+ constructor(options, io = realIo()) {
24
+ this.io = io;
25
+ this.dryRun = options.dryRun ?? false;
26
+ this.quiet = options.quiet ?? false;
27
+ this.verbose = this.quiet ? -1 : (options.verbose ?? 0);
28
+ this.yes = options.yes ?? false;
29
+ this.json = options.json ?? false;
30
+ }
31
+ // --- narration (stderr) ---------------------------------------------------
32
+ /** One line, if the verbosity allows the level (0 normal, 1 verbose, 2 debug). */
33
+ say(line, level = 0) {
34
+ if (this.verbose >= level)
35
+ this.io.stderr.write(`${line}\n`);
36
+ }
37
+ /** A final outcome line, `✓ ...` or `✗ ...`; shown even under --quiet. */
38
+ done(line, ok = true) {
39
+ this.io.stderr.write(`${ok ? '✓' : '✗'} ${line}\n`);
40
+ }
41
+ // --- results (stdout) -----------------------------------------------------
42
+ /**
43
+ * The command's product. `data` is what --json prints; `human` renders it
44
+ * otherwise (default: strings as they are, arrays one item per line).
45
+ */
46
+ result(data, human = renderHuman) {
47
+ const text = this.json ? JSON.stringify(data, null, 2) : human(data);
48
+ this.io.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
49
+ }
50
+ // --- reads: always performed ---------------------------------------------
51
+ /** Run a command for its output. Changes nothing; narrated only when verbose. */
52
+ read(command, args = [], options = {}) {
53
+ this.say(`? ${describe({ kind: 'run', command, args, ...options })}`, 1);
54
+ return this.io.exec(command, args, options);
55
+ }
56
+ readFile(path) {
57
+ this.say(`? read ${path}`, 1);
58
+ return this.io.readFile(path);
59
+ }
60
+ readDir(path) {
61
+ this.say(`? list ${path}`, 2);
62
+ return this.io.readDir(path);
63
+ }
64
+ // --- effects: narrated always, performed unless --dry-run -----------------
65
+ /**
66
+ * Ask before applying. `--yes` and `--dry-run` answer yes; off a terminal
67
+ * without `--yes` the answer is an error, never a hang.
68
+ */
69
+ async confirm(question) {
70
+ if (this.yes || this.dryRun) {
71
+ this.say(`? ${question} yes (${this.yes ? '--yes' : '--dry-run'})`, 1);
72
+ return true;
73
+ }
74
+ if (!this.io.prompt)
75
+ throw new Error(`cannot ask "${question}" without a terminal; pass --yes`);
76
+ return this.io.prompt(question);
77
+ }
78
+ /** Narrate and perform each step in order; the first failure stops the run. */
79
+ async apply(plan) {
80
+ for (const step of plan) {
81
+ this.say(`${this.dryRun ? '[dry-run] ' : ''}${describe(step)}`);
82
+ const input = body(step);
83
+ if (input !== undefined)
84
+ this.say(indent(input), step.kind === 'write' ? 2 : 1);
85
+ if (!this.dryRun)
86
+ await this.perform(step);
87
+ }
88
+ }
89
+ perform(step) {
90
+ const inherit = this.verbose >= 2;
91
+ switch (step.kind) {
92
+ case 'run':
93
+ return this.io.exec(step.command, step.args, { cwd: step.cwd, input: step.input, inherit });
94
+ case 'ssh':
95
+ return this.io.exec('ssh', [step.host, 'bash', '-s'], { input: step.script, inherit });
96
+ case 'write':
97
+ return this.io.writeFile(step.path, step.content);
98
+ }
99
+ }
100
+ }
101
+ function renderHuman(data) {
102
+ if (typeof data === 'string')
103
+ return data;
104
+ if (Array.isArray(data))
105
+ return data.map(String).join('\n');
106
+ return JSON.stringify(data, null, 2);
107
+ }
108
+ function indent(text) {
109
+ return text
110
+ .trimEnd()
111
+ .split('\n')
112
+ .map((l) => ` ${l}`)
113
+ .join('\n');
114
+ }
115
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const ANSWERS_FILE = ".copier-answers.yml";
2
+ export declare const TEMPLATE_FILES: string[];
3
+ export interface Answers {
4
+ /** The template's ref the project was last updated to, if recorded. */
5
+ commit?: string;
6
+ /** Where the template comes from, e.g. `gh:plexus-ms/preset-repo-web`. */
7
+ src?: string;
8
+ /** Every other answer, by question name. */
9
+ answers: Record<string, unknown>;
10
+ }
11
+ export declare function parseAnswers(text: string): Answers;
12
+ //# sourceMappingURL=copier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copier.d.ts","sourceRoot":"","sources":["../src/copier.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAClD,eAAO,MAAM,cAAc,UAAgC,CAAC;AAE5D,MAAM,WAAW,OAAO;IACtB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUlD"}
package/dist/copier.js ADDED
@@ -0,0 +1,19 @@
1
+ // Copier's bookkeeping in a generated project (§ 3 PLX-Updates): the answers
2
+ // file records the template (`_src_path`) and the version it was last synced
3
+ // to (`_commit`), plus the answers given. A template is a directory carrying a
4
+ // `copier.yml`.
5
+ import { parse } from 'yaml';
6
+ export const ANSWERS_FILE = '.copier-answers.yml';
7
+ export const TEMPLATE_FILES = ['copier.yml', 'copier.yaml'];
8
+ export function parseAnswers(text) {
9
+ const data = parse(text);
10
+ if (data === null || typeof data !== 'object' || Array.isArray(data))
11
+ throw new Error('answers file is not a mapping');
12
+ const { _commit, _src_path, ...answers } = data;
13
+ return {
14
+ commit: _commit === undefined ? undefined : String(_commit),
15
+ src: _src_path === undefined ? undefined : String(_src_path),
16
+ answers,
17
+ };
18
+ }
19
+ //# sourceMappingURL=copier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copier.js","sourceRoot":"","sources":["../src/copier.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,gBAAgB;AAChB,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;AAW5D,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAY,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,GAAG,IAA+B,CAAC;IAC3E,OAAO;QACL,MAAM,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAC3D,GAAG,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAC5D,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { Context, type Options } from './context.ts';
2
+ export { ANSWERS_FILE, type Answers, parseAnswers, TEMPLATE_FILES } from './copier.ts';
3
+ export { type DirEntry, ExecError, type ExecOptions, type Io, realIo, render } from './io.ts';
4
+ export { type Declaration, MARKER_FILE, parseMarker, splitFrontmatter } from './marker.ts';
5
+ export { type Directory, type GitFacet, isNotable, type Kind, kindOf, shortRemote, type TemplateFacet, } from './model.ts';
6
+ export { overview, PRUNED } from './overview.ts';
7
+ export { body, describe, type Plan, run, type Step, ssh, write } from './plan.ts';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,SAAS,EACT,KAAK,IAAI,EACT,MAAM,EACN,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // @plexus-ms/lib: the core layer of plx, usable without the CLI.
2
+ //
3
+ // A verb here is either a reader returning data, or a pure planner returning
4
+ // a Plan (plan.ts) from what was read. The Context (context.ts) reads through
5
+ // an injected Io (io.ts) and applies plans, narrating every step; the CLI
6
+ // package is an adapter that maps argv onto this. The parsers of Plexus's own
7
+ // file shapes (marker.ts, copier.ts) live here, once, for every verb.
8
+ // Conventions: compendium/miscellaneous/cli-design.md.
9
+ export { Context } from "./context.js";
10
+ export { ANSWERS_FILE, parseAnswers, TEMPLATE_FILES } from "./copier.js";
11
+ export { ExecError, realIo, render } from "./io.js";
12
+ export { MARKER_FILE, parseMarker, splitFrontmatter } from "./marker.js";
13
+ export { isNotable, kindOf, shortRemote, } from "./model.js";
14
+ export { overview, PRUNED } from "./overview.js";
15
+ export { body, describe, run, ssh, write } from "./plan.js";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,0EAA0E;AAC1E,8EAA8E;AAC9E,sEAAsE;AACtE,uDAAuD;AACvD,OAAO,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,YAAY,EAAgB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EAAiB,SAAS,EAA6B,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,EAAoB,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAGL,SAAS,EAET,MAAM,EACN,WAAW,GAEZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAa,GAAG,EAAa,GAAG,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC"}
package/dist/io.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { Writable } from 'node:stream';
2
+ export interface ExecOptions {
3
+ cwd?: string;
4
+ env?: NodeJS.ProcessEnv;
5
+ /** Fed to the process's stdin, then closed. */
6
+ input?: string;
7
+ /** Pass the process's output through to the terminal instead of capturing it. */
8
+ inherit?: boolean;
9
+ }
10
+ export interface Io {
11
+ stdout: Writable;
12
+ stderr: Writable;
13
+ /**
14
+ * Ask a yes/no question of a human, or undefined when there is none: off a
15
+ * terminal, or in a program that brings no prompt (the CLI supplies one).
16
+ */
17
+ prompt?: (question: string) => Promise<boolean>;
18
+ /** Run a process to completion; resolves with its stdout, rejects with ExecError on nonzero exit. */
19
+ exec(command: string, args: string[], options?: ExecOptions): Promise<string>;
20
+ readFile(path: string): Promise<string>;
21
+ writeFile(path: string, content: string): Promise<void>;
22
+ /** The entries of a directory, in no particular order. */
23
+ readDir(path: string): Promise<DirEntry[]>;
24
+ }
25
+ export interface DirEntry {
26
+ name: string;
27
+ directory: boolean;
28
+ }
29
+ export declare class ExecError extends Error {
30
+ readonly command: string;
31
+ readonly code: number | null;
32
+ readonly stderr: string;
33
+ constructor(command: string, code: number | null, stderr: string);
34
+ }
35
+ /** True when a human is on both ends of the terminal. */
36
+ export declare const isInteractive: () => boolean;
37
+ export declare function realIo(prompt?: Io['prompt']): Io;
38
+ /** The command line as a shell would need it typed: args quoted when they must be. */
39
+ export declare function render(command: string, args: string[]): string;
40
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,EAAE;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,qGAAqG;IACrG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,0DAA0D;IAC1D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM;CAMjE;AAED,yDAAyD;AACzD,eAAO,MAAM,aAAa,QAAO,OAAwE,CAAC;AAE1G,wBAAgB,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAiChD;AAED,sFAAsF;AACtF,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAE9D"}
package/dist/io.js ADDED
@@ -0,0 +1,62 @@
1
+ // Everything a verb does to the world goes through an Io, injected into the
2
+ // Context (context.ts). The real one wraps node; a test hands in a fake and
3
+ // asserts what was asked of it, with no subprocess and no filesystem.
4
+ import { spawn } from 'node:child_process';
5
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
6
+ export class ExecError extends Error {
7
+ command;
8
+ code;
9
+ stderr;
10
+ constructor(command, code, stderr) {
11
+ super(`${command} exited with ${code ?? 'signal'}${stderr ? `\n${stderr.trimEnd()}` : ''}`);
12
+ this.command = command;
13
+ this.code = code;
14
+ this.stderr = stderr;
15
+ }
16
+ }
17
+ /** True when a human is on both ends of the terminal. */
18
+ export const isInteractive = () => process.stdin.isTTY === true && process.stderr.isTTY === true;
19
+ export function realIo(prompt) {
20
+ return {
21
+ stdout: process.stdout,
22
+ stderr: process.stderr,
23
+ prompt: prompt && isInteractive() ? prompt : undefined,
24
+ exec: (command, args, options = {}) => new Promise((resolve, reject) => {
25
+ const child = spawn(command, args, {
26
+ cwd: options.cwd,
27
+ env: options.env,
28
+ stdio: [options.input === undefined ? 'ignore' : 'pipe', options.inherit ? 'inherit' : 'pipe', 'pipe'],
29
+ });
30
+ let stdout = '';
31
+ let stderr = '';
32
+ child.stdout?.on('data', (chunk) => {
33
+ stdout += chunk;
34
+ });
35
+ child.stderr?.on('data', (chunk) => {
36
+ stderr += chunk;
37
+ if (options.inherit)
38
+ process.stderr.write(chunk);
39
+ });
40
+ child.on('error', reject);
41
+ child.on('close', (code) => {
42
+ if (code === 0)
43
+ resolve(stdout);
44
+ else
45
+ reject(new ExecError(render(command, args), code, stderr));
46
+ });
47
+ if (options.input !== undefined)
48
+ child.stdin?.end(options.input);
49
+ }),
50
+ readFile: (path) => readFile(path, 'utf8'),
51
+ writeFile: (path, content) => writeFile(path, content),
52
+ readDir: async (path) => (await readdir(path, { withFileTypes: true })).map((e) => ({ name: e.name, directory: e.isDirectory() })),
53
+ };
54
+ }
55
+ /** The command line as a shell would need it typed: args quoted when they must be. */
56
+ export function render(command, args) {
57
+ return [command, ...args].map(quote).join(' ');
58
+ }
59
+ function quote(arg) {
60
+ return /^[\w./:@%+=,-]+$/.test(arg) ? arg : `'${arg.replaceAll("'", "'\\''")}'`;
61
+ }
62
+ //# sourceMappingURL=io.js.map
package/dist/io.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface Declaration {
2
+ /** The Plexus version the unit was last brought up to, e.g. `0.62.0`. */
3
+ plexus: string;
4
+ /** The module codes it follows, e.g. `PLX-Repo`. */
5
+ modules: string[];
6
+ /** The prose after the frontmatter: what the unit is, and its owned deviations. */
7
+ body: string;
8
+ }
9
+ export declare const MARKER_FILE = "PLEXUS.md";
10
+ export declare function parseMarker(text: string): Declaration;
11
+ /** The YAML between the leading `---` lines, as a plain object, and the rest. */
12
+ export declare function splitFrontmatter(text: string): {
13
+ data: Record<string, unknown>;
14
+ body: string;
15
+ };
16
+ //# sourceMappingURL=marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.d.ts","sourceRoot":"","sources":["../src/marker.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,WAAW,cAAc,CAAC;AAEvC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAQrD;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAM9F"}
package/dist/marker.js ADDED
@@ -0,0 +1,26 @@
1
+ // The declaration marker, PLEXUS.md (§ 1.4 PLX): YAML frontmatter with the
2
+ // Plexus version and the declared modules, then free-form prose. This is the
3
+ // one reader of that shape; every verb that needs a marker parses it here.
4
+ import { parse } from 'yaml';
5
+ export const MARKER_FILE = 'PLEXUS.md';
6
+ export function parseMarker(text) {
7
+ const { data, body } = splitFrontmatter(text);
8
+ const plexus = data.plexus;
9
+ const modules = data.modules;
10
+ if (typeof plexus !== 'string' && typeof plexus !== 'number')
11
+ throw new Error('marker: `plexus` must be a version');
12
+ if (!Array.isArray(modules) || !modules.every((m) => typeof m === 'string'))
13
+ throw new Error('marker: `modules` must be a list of module codes');
14
+ return { plexus: String(plexus), modules, body };
15
+ }
16
+ /** The YAML between the leading `---` lines, as a plain object, and the rest. */
17
+ export function splitFrontmatter(text) {
18
+ const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(text);
19
+ if (!match)
20
+ throw new Error('no frontmatter: expected a `---` block at the top');
21
+ const data = parse(match[1] ?? '');
22
+ if (data === null || typeof data !== 'object' || Array.isArray(data))
23
+ throw new Error('frontmatter is not a mapping');
24
+ return { data: data, body: match[2] ?? '' };
25
+ }
26
+ //# sourceMappingURL=marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.js","sourceRoot":"","sources":["../src/marker.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAW7B,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AAEvC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC7B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACpH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACjF,MAAM,IAAI,GAAY,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACtH,OAAO,EAAE,IAAI,EAAE,IAA+B,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { Answers } from './copier.ts';
2
+ import type { Declaration } from './marker.ts';
3
+ export interface Directory {
4
+ name: string;
5
+ path: string;
6
+ git?: GitFacet;
7
+ template?: TemplateFacet;
8
+ copier?: Answers;
9
+ declaration?: Declaration;
10
+ /** Marker files that could not be read; shown, never fatal. */
11
+ problems?: string[];
12
+ children: Directory[];
13
+ }
14
+ export interface GitFacet {
15
+ /** The checked-out branch, or the short commit when detached. */
16
+ branch: string;
17
+ /** Uncommitted tracked or untracked changes. */
18
+ dirty: boolean;
19
+ /** The most recent tag reachable from the default branch, if any. */
20
+ tag?: string;
21
+ /** The `origin` remote, shortened: `gh:org/repo` for GitHub. */
22
+ remote?: string;
23
+ }
24
+ export interface TemplateFacet {
25
+ /** The template repo's most recent tag, if any. */
26
+ tag?: string;
27
+ }
28
+ export type Kind = 'app' | 'platform' | 'repo';
29
+ /** What a declaration makes a directory, by the modules it follows. */
30
+ export declare function kindOf(declaration: Declaration): Kind | undefined;
31
+ /** A git remote URL as a short handle: `gh:org/repo` for GitHub, the URL otherwise. */
32
+ export declare function shortRemote(url: string): string;
33
+ /** Whether a directory, or anything below it, carries a facet worth showing. */
34
+ export declare function isNotable(dir: Directory): boolean;
35
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;AAE/C,uEAAuE;AACvE,wBAAgB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAMjE;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAEjD"}
package/dist/model.js ADDED
@@ -0,0 +1,22 @@
1
+ /** What a declaration makes a directory, by the modules it follows. */
2
+ export function kindOf(declaration) {
3
+ const has = (code) => declaration.modules.includes(code);
4
+ if (has('PLX-App'))
5
+ return 'app';
6
+ if (has('PLX-Platform'))
7
+ return 'platform';
8
+ if (has('PLX-Repo'))
9
+ return 'repo';
10
+ return undefined;
11
+ }
12
+ /** A git remote URL as a short handle: `gh:org/repo` for GitHub, the URL otherwise. */
13
+ export function shortRemote(url) {
14
+ const bare = url.replace(/\.git$/, '').replace(/\/$/, '');
15
+ const m = /^(?:git@github\.com:|github\.com:|ssh:\/\/git@github\.com\/|https?:\/\/github\.com\/)(.+)$/.exec(bare);
16
+ return m ? `gh:${m[1]}` : bare;
17
+ }
18
+ /** Whether a directory, or anything below it, carries a facet worth showing. */
19
+ export function isNotable(dir) {
20
+ return Boolean(dir.git || dir.template || dir.copier || dir.declaration || dir.problems?.length);
21
+ }
22
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAqCA,uEAAuE;AACvE,MAAM,UAAU,MAAM,CAAC,WAAwB;IAC7C,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,GAAG,CAAC,cAAc,CAAC;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,MAAM,CAAC;IACnC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,4FAA4F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClH,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,GAAc;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACnG,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Context } from './context.ts';
2
+ import { type Directory } from './model.ts';
3
+ /** Heavy or generated directories never descended into. `.git` is the repo marker and is handled apart. */
4
+ export declare const PRUNED: Set<string>;
5
+ export declare function overview(ctx: Context, root: string): Promise<Directory>;
6
+ //# sourceMappingURL=overview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../src/overview.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,EAAE,KAAK,SAAS,EAA6D,MAAM,YAAY,CAAC;AAEvG,2GAA2G;AAC3G,eAAO,MAAM,MAAM,aAsBjB,CAAC;AAEH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAG7E"}
@@ -0,0 +1,119 @@
1
+ // overview: the map of a folder of repos (the toolbox's overview.sh, moved).
2
+ //
3
+ // Walk a directory, note for every directory whether it is a git repo, a
4
+ // copier template, a copier-managed project, or a declared Plexus unit, and
5
+ // return the tree of directories that carry such a facet, with the ancestors
6
+ // needed to reach them. Reads only: there is nothing to plan or apply.
7
+ //
8
+ // plx overview # map the current directory
9
+ // plx overview ~/dev # map somewhere else
10
+ import { join } from 'node:path';
11
+ import { ANSWERS_FILE, parseAnswers, TEMPLATE_FILES } from "./copier.js";
12
+ import { MARKER_FILE, parseMarker } from "./marker.js";
13
+ import { isNotable, shortRemote } from "./model.js";
14
+ /** Heavy or generated directories never descended into. `.git` is the repo marker and is handled apart. */
15
+ export const PRUNED = new Set([
16
+ 'node_modules',
17
+ '.hg',
18
+ '.svn',
19
+ '.next',
20
+ '.nuxt',
21
+ '.turbo',
22
+ '.svelte-kit',
23
+ 'dist',
24
+ 'build',
25
+ 'out',
26
+ '.output',
27
+ '.venv',
28
+ 'venv',
29
+ '__pycache__',
30
+ 'target',
31
+ '.cache',
32
+ '.pnpm-store',
33
+ 'coverage',
34
+ 'vendor',
35
+ '.terraform',
36
+ '.gradle',
37
+ ]);
38
+ export async function overview(ctx, root) {
39
+ const dir = await walk(ctx, root, root.replace(/\/+$/, '').split('/').pop() || root);
40
+ return dir;
41
+ }
42
+ async function walk(ctx, path, name) {
43
+ const entries = await ctx.readDir(path);
44
+ const files = new Set(entries.filter((e) => !e.directory).map((e) => e.name));
45
+ const names = new Set(entries.map((e) => e.name));
46
+ const dir = { name, path, children: [] };
47
+ // `.git` is a directory for a checkout and a file for a worktree or submodule.
48
+ if (names.has('.git'))
49
+ dir.git = await gitFacet(ctx, path);
50
+ if (TEMPLATE_FILES.some((f) => files.has(f)))
51
+ dir.template = await templateFacet(ctx, path);
52
+ if (files.has(ANSWERS_FILE)) {
53
+ await facet(ctx, dir, ANSWERS_FILE, (text) => {
54
+ dir.copier = parseAnswers(text);
55
+ });
56
+ }
57
+ if (files.has(MARKER_FILE)) {
58
+ await facet(ctx, dir, MARKER_FILE, (text) => {
59
+ dir.declaration = parseMarker(text);
60
+ });
61
+ }
62
+ const subdirs = entries
63
+ .filter((e) => e.directory && e.name !== '.git' && !PRUNED.has(e.name))
64
+ .map((e) => e.name)
65
+ .sort();
66
+ for (const sub of subdirs) {
67
+ const child = await walk(ctx, join(path, sub), sub);
68
+ if (isNotable(child) || child.children.length > 0)
69
+ dir.children.push(child);
70
+ }
71
+ return dir;
72
+ }
73
+ async function facet(ctx, dir, file, read) {
74
+ try {
75
+ read(await ctx.readFile(join(dir.path, file)));
76
+ }
77
+ catch (error) {
78
+ dir.problems ??= [];
79
+ dir.problems.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
80
+ }
81
+ }
82
+ /** A git read that may legitimately fail (no remote, no tag, detached): undefined then. */
83
+ async function git(ctx, cwd, args) {
84
+ try {
85
+ const out = (await ctx.read('git', args, { cwd })).trim();
86
+ return out === '' ? undefined : out;
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ }
92
+ async function gitFacet(ctx, cwd) {
93
+ const branch = (await git(ctx, cwd, ['symbolic-ref', '--short', '-q', 'HEAD'])) ??
94
+ (await git(ctx, cwd, ['rev-parse', '--short', 'HEAD'])) ??
95
+ '?';
96
+ const dirty = (await git(ctx, cwd, ['status', '--porcelain'])) !== undefined;
97
+ const remote = await git(ctx, cwd, ['remote', 'get-url', 'origin']);
98
+ // The most recent tag reachable from the default branch: origin/HEAD, else a local main or master.
99
+ let defaultRef = await git(ctx, cwd, ['symbolic-ref', '--short', '-q', 'refs/remotes/origin/HEAD']);
100
+ for (const b of ['main', 'master']) {
101
+ if (defaultRef)
102
+ break;
103
+ if (await git(ctx, cwd, ['rev-parse', '--verify', '-q', `refs/heads/${b}`]))
104
+ defaultRef = b;
105
+ }
106
+ const tag = defaultRef ? await git(ctx, cwd, ['describe', '--tags', '--abbrev=0', defaultRef]) : undefined;
107
+ const facet = { branch, dirty };
108
+ if (tag)
109
+ facet.tag = tag;
110
+ if (remote)
111
+ facet.remote = shortRemote(remote);
112
+ return facet;
113
+ }
114
+ async function templateFacet(ctx, cwd) {
115
+ const tag = (await git(ctx, cwd, ['describe', '--tags', '--abbrev=0'])) ??
116
+ (await git(ctx, cwd, ['tag', '--list', '--sort=-v:refname']))?.split('\n')[0];
117
+ return tag ? { tag } : {};
118
+ }
119
+ //# sourceMappingURL=overview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overview.js","sourceRoot":"","sources":["../src/overview.ts"],"names":[],"mappings":"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"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type Step = {
2
+ kind: 'run';
3
+ command: string;
4
+ args: string[];
5
+ cwd?: string;
6
+ input?: string;
7
+ } | {
8
+ kind: 'ssh';
9
+ host: string;
10
+ script: string;
11
+ } | {
12
+ kind: 'write';
13
+ path: string;
14
+ content: string;
15
+ };
16
+ export type Plan = Step[];
17
+ export declare const run: (command: string, args?: string[], options?: {
18
+ cwd?: string;
19
+ input?: string;
20
+ }) => Step;
21
+ /** Feed a bash script to a host: the remote half of a verb stays bash, carried verbatim. */
22
+ export declare const ssh: (host: string, script: string) => Step;
23
+ export declare const write: (path: string, content: string) => Step;
24
+ /** One line, the shell equivalent of the step. */
25
+ export declare function describe(step: Step): string;
26
+ /** What a step feeds in, shown at higher verbosity under its description. */
27
+ export declare function body(step: Step): string | undefined;
28
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,IAAI,GACZ;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;AAE1B,eAAO,MAAM,GAAG,GAAI,SAAS,MAAM,EAAE,OAAM,MAAM,EAAO,EAAE,UAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,KAAG,IAKzG,CAAC;AAEH,4FAA4F;AAC5F,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,KAAG,IAAuC,CAAC;AAE3F,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,IAA0C,CAAC;AAEjG,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAS3C;AAED,6EAA6E;AAC7E,wBAAgB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CASnD"}
package/dist/plan.js ADDED
@@ -0,0 +1,40 @@
1
+ // A Plan is what a command decides to do, as data: the effects, in order.
2
+ //
3
+ // A command's core computes a Plan from what it read (pure, testable by
4
+ // comparing plans), the Context applies it (context.ts). Printing a plan is
5
+ // the dry run; applying it is the real run; both come from the one Plan, so
6
+ // they cannot drift. Describing a step gives the exact shell a stranded
7
+ // operator would type instead.
8
+ import { render } from "./io.js";
9
+ export const run = (command, args = [], options = {}) => ({
10
+ kind: 'run',
11
+ command,
12
+ args,
13
+ ...options,
14
+ });
15
+ /** Feed a bash script to a host: the remote half of a verb stays bash, carried verbatim. */
16
+ export const ssh = (host, script) => ({ kind: 'ssh', host, script });
17
+ export const write = (path, content) => ({ kind: 'write', path, content });
18
+ /** One line, the shell equivalent of the step. */
19
+ export function describe(step) {
20
+ switch (step.kind) {
21
+ case 'run':
22
+ return `$ ${step.cwd ? `(cd ${step.cwd} && ` : ''}${render(step.command, step.args)}${step.cwd ? ')' : ''}`;
23
+ case 'ssh':
24
+ return `$ ${render('ssh', [step.host, 'bash', '-s'])} <<'EOF'`;
25
+ case 'write':
26
+ return `> ${step.path} (${Buffer.byteLength(step.content)} bytes)`;
27
+ }
28
+ }
29
+ /** What a step feeds in, shown at higher verbosity under its description. */
30
+ export function body(step) {
31
+ switch (step.kind) {
32
+ case 'run':
33
+ return step.input;
34
+ case 'ssh':
35
+ return `${step.script.trimEnd()}\nEOF`;
36
+ case 'write':
37
+ return step.content;
38
+ }
39
+ }
40
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,+BAA+B;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AASjC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,OAAiB,EAAE,EAAE,UAA4C,EAAE,EAAQ,EAAE,CAAC,CAAC;IAClH,IAAI,EAAE,KAAK;IACX,OAAO;IACP,IAAI;IACJ,GAAG,OAAO;CACX,CAAC,CAAC;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,MAAc,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAE3F,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,OAAe,EAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAEjG,kDAAkD;AAClD,MAAM,UAAU,QAAQ,CAAC,IAAU;IACjC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,KAAK;YACR,OAAO,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC9G,KAAK,KAAK;YACR,OAAO,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC;QACjE,KAAK,OAAO;YACV,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACvE,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,IAAI,CAAC,IAAU;IAC7B,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,KAAK;YACR,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QACzC,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@plexus-ms/lib",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "The Plexus verbs as a library: the plan-then-apply core behind plx, and the one parser of Plexus's own file shapes",
5
+ "license": "GPL-3.0-or-later",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/plexus-ms/packages.git",
20
+ "directory": "packages/lib"
21
+ },
22
+ "publishConfig": {
23
+ "registry": "https://registry.npmjs.org",
24
+ "access": "public"
25
+ },
26
+ "engines": {
27
+ "node": ">=24"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "check": "tsc --noEmit",
32
+ "test": "node --test",
33
+ "prepack": "tsc"
34
+ },
35
+ "dependencies": {
36
+ "yaml": "^2.9.1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^26.1.2",
40
+ "typescript": "catalog:"
41
+ }
42
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.