stego-cli 0.4.1 → 0.4.2
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 +6 -0
- package/dist/shared/src/contracts/cli/envelopes.js +19 -0
- package/dist/shared/src/contracts/cli/errors.js +14 -0
- package/dist/shared/src/contracts/cli/exit-codes.js +15 -0
- package/dist/shared/src/contracts/cli/index.js +6 -0
- package/dist/shared/src/contracts/cli/metadata.js +1 -0
- package/dist/shared/src/contracts/cli/operations.js +1 -0
- package/dist/shared/src/domain/comments/anchors.js +1 -0
- package/dist/shared/src/domain/comments/index.js +4 -0
- package/dist/shared/src/domain/comments/serializer.js +1 -0
- package/dist/shared/src/domain/comments/thread-key.js +21 -0
- package/dist/shared/src/domain/frontmatter/index.js +3 -0
- package/dist/shared/src/domain/frontmatter/parser.js +34 -0
- package/dist/shared/src/domain/frontmatter/serializer.js +32 -0
- package/dist/shared/src/domain/frontmatter/validators.js +47 -0
- package/dist/shared/src/domain/project/index.js +4 -0
- package/dist/shared/src/domain/stages/index.js +20 -0
- package/dist/shared/src/index.js +6 -0
- package/dist/shared/src/utils/guards.js +6 -0
- package/dist/shared/src/utils/index.js +3 -0
- package/dist/shared/src/utils/invariant.js +5 -0
- package/dist/shared/src/utils/result.js +6 -0
- package/dist/stego-cli/src/app/cli-version.js +32 -0
- package/dist/stego-cli/src/app/command-context.js +1 -0
- package/dist/stego-cli/src/app/command-registry.js +121 -0
- package/dist/stego-cli/src/app/create-cli-app.js +42 -0
- package/dist/stego-cli/src/app/error-boundary.js +42 -0
- package/dist/stego-cli/src/app/index.js +6 -0
- package/dist/stego-cli/src/app/output-renderer.js +6 -0
- package/dist/stego-cli/src/main.js +14 -0
- package/dist/stego-cli/src/modules/comments/application/comment-operations.js +457 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-add.js +40 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-clear-resolved.js +32 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-delete.js +33 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-read.js +32 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-reply.js +36 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-set-status.js +35 -0
- package/dist/stego-cli/src/modules/comments/commands/comments-sync-anchors.js +33 -0
- package/dist/stego-cli/src/modules/comments/domain/comment-policy.js +14 -0
- package/dist/stego-cli/src/modules/comments/index.js +20 -0
- package/dist/stego-cli/src/modules/comments/infra/comments-repo.js +68 -0
- package/dist/stego-cli/src/modules/comments/types.js +1 -0
- package/dist/stego-cli/src/modules/compile/application/compile-manuscript.js +16 -0
- package/dist/stego-cli/src/modules/compile/commands/build.js +51 -0
- package/dist/stego-cli/src/modules/compile/domain/compile-structure.js +105 -0
- package/dist/stego-cli/src/modules/compile/index.js +8 -0
- package/dist/stego-cli/src/modules/compile/infra/dist-writer.js +8 -0
- package/dist/stego-cli/src/modules/compile/types.js +1 -0
- package/dist/stego-cli/src/modules/export/application/run-export.js +29 -0
- package/dist/stego-cli/src/modules/export/commands/export.js +61 -0
- package/dist/stego-cli/src/modules/export/domain/exporter.js +6 -0
- package/dist/stego-cli/src/modules/export/index.js +8 -0
- package/dist/stego-cli/src/modules/export/types.js +1 -0
- package/dist/stego-cli/src/modules/index.js +22 -0
- package/dist/stego-cli/src/modules/manuscript/application/create-manuscript.js +107 -0
- package/dist/stego-cli/src/modules/manuscript/application/order-inference.js +56 -0
- package/dist/stego-cli/src/modules/manuscript/commands/new-manuscript.js +54 -0
- package/dist/stego-cli/src/modules/manuscript/domain/manuscript.js +1 -0
- package/dist/stego-cli/src/modules/manuscript/index.js +9 -0
- package/dist/stego-cli/src/modules/manuscript/infra/manuscript-repo.js +39 -0
- package/dist/stego-cli/src/modules/manuscript/types.js +1 -0
- package/dist/stego-cli/src/modules/metadata/application/apply-metadata.js +45 -0
- package/dist/stego-cli/src/modules/metadata/application/read-metadata.js +18 -0
- package/dist/stego-cli/src/modules/metadata/commands/metadata-apply.js +38 -0
- package/dist/stego-cli/src/modules/metadata/commands/metadata-read.js +33 -0
- package/dist/stego-cli/src/modules/metadata/domain/metadata.js +1 -0
- package/dist/stego-cli/src/modules/metadata/index.js +11 -0
- package/dist/stego-cli/src/modules/metadata/infra/metadata-repo.js +68 -0
- package/dist/stego-cli/src/modules/metadata/types.js +1 -0
- package/dist/stego-cli/src/modules/project/application/create-project.js +203 -0
- package/dist/stego-cli/src/modules/project/application/infer-project.js +72 -0
- package/dist/stego-cli/src/modules/project/commands/new-project.js +86 -0
- package/dist/stego-cli/src/modules/project/domain/project.js +1 -0
- package/dist/stego-cli/src/modules/project/index.js +9 -0
- package/dist/stego-cli/src/modules/project/infra/project-repo.js +27 -0
- package/dist/stego-cli/src/modules/project/types.js +1 -0
- package/dist/stego-cli/src/modules/quality/application/inspect-project.js +603 -0
- package/dist/stego-cli/src/modules/quality/application/lint-runner.js +313 -0
- package/dist/stego-cli/src/modules/quality/application/stage-check.js +87 -0
- package/dist/stego-cli/src/modules/quality/commands/check-stage.js +54 -0
- package/dist/stego-cli/src/modules/quality/commands/lint.js +51 -0
- package/dist/stego-cli/src/modules/quality/commands/validate.js +50 -0
- package/dist/stego-cli/src/modules/quality/domain/issues.js +1 -0
- package/dist/stego-cli/src/modules/quality/domain/policies.js +1 -0
- package/dist/stego-cli/src/modules/quality/index.js +14 -0
- package/dist/stego-cli/src/modules/quality/infra/cspell-adapter.js +1 -0
- package/dist/stego-cli/src/modules/quality/infra/markdownlint-adapter.js +1 -0
- package/dist/stego-cli/src/modules/quality/types.js +1 -0
- package/dist/stego-cli/src/modules/scaffold/application/scaffold-workspace.js +307 -0
- package/dist/stego-cli/src/modules/scaffold/commands/init.js +34 -0
- package/dist/stego-cli/src/modules/scaffold/domain/templates.js +182 -0
- package/dist/stego-cli/src/modules/scaffold/index.js +8 -0
- package/dist/stego-cli/src/modules/scaffold/infra/template-repo.js +33 -0
- package/dist/stego-cli/src/modules/scaffold/types.js +1 -0
- package/dist/stego-cli/src/modules/spine/application/create-category.js +14 -0
- package/dist/stego-cli/src/modules/spine/application/create-entry.js +9 -0
- package/dist/stego-cli/src/modules/spine/application/read-catalog.js +13 -0
- package/dist/stego-cli/src/modules/spine/commands/spine-deprecated-aliases.js +33 -0
- package/dist/stego-cli/src/modules/spine/commands/spine-new-category.js +64 -0
- package/dist/stego-cli/src/modules/spine/commands/spine-new-entry.js +65 -0
- package/dist/stego-cli/src/modules/spine/commands/spine-read.js +49 -0
- package/dist/{spine/spine-domain.js → stego-cli/src/modules/spine/domain/spine.js} +13 -7
- package/dist/stego-cli/src/modules/spine/index.js +16 -0
- package/dist/stego-cli/src/modules/spine/infra/spine-repo.js +46 -0
- package/dist/stego-cli/src/modules/spine/types.js +1 -0
- package/dist/stego-cli/src/modules/workspace/application/discover-projects.js +18 -0
- package/dist/stego-cli/src/modules/workspace/application/resolve-workspace.js +73 -0
- package/dist/stego-cli/src/modules/workspace/commands/list-projects.js +40 -0
- package/dist/stego-cli/src/modules/workspace/index.js +9 -0
- package/dist/stego-cli/src/modules/workspace/infra/workspace-repo.js +37 -0
- package/dist/stego-cli/src/modules/workspace/types.js +1 -0
- package/dist/stego-cli/src/platform/clock.js +3 -0
- package/dist/stego-cli/src/platform/fs.js +13 -0
- package/dist/stego-cli/src/platform/index.js +3 -0
- package/dist/stego-cli/src/platform/temp-files.js +6 -0
- package/package.json +20 -11
- package/dist/comments/comments-command.js +0 -499
- package/dist/comments/errors.js +0 -20
- package/dist/metadata/metadata-command.js +0 -127
- package/dist/metadata/metadata-domain.js +0 -209
- package/dist/spine/spine-command.js +0 -129
- package/dist/stego-cli.js +0 -2264
- /package/dist/{exporters/exporter-types.js → shared/src/contracts/cli/comments.js} +0 -0
- /package/dist/{comments/comment-domain.js → shared/src/domain/comments/parser.js} +0 -0
- /package/dist/{exporters → stego-cli/src/modules/export/infra}/markdown-exporter.js +0 -0
- /package/dist/{exporters → stego-cli/src/modules/export/infra}/pandoc-exporter.js +0 -0
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { addCommentToState, clearResolvedInState, deleteCommentInState, ensureNoParseErrors, loadCommentDocumentState, renderStateDocument, replyToCommentInState, serializeLoadedState, setCommentStatusInState, syncAnchorsInState } from "./comment-domain.js";
|
|
4
|
-
import { CommentsCommandError } from "./errors.js";
|
|
5
|
-
/** Handles `stego comments ...` command group. */
|
|
6
|
-
export async function runCommentsCommand(options, cwd) {
|
|
7
|
-
const [subcommand, manuscriptArg] = options._;
|
|
8
|
-
if (!subcommand) {
|
|
9
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Comments subcommand is required. Use: read, add, reply, set-status, delete, clear-resolved, sync-anchors.");
|
|
10
|
-
}
|
|
11
|
-
if (!manuscriptArg) {
|
|
12
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Manuscript path is required. Use: stego comments <subcommand> <manuscript> ...");
|
|
13
|
-
}
|
|
14
|
-
const outputFormat = parseOutputFormat(readString(options, "format"));
|
|
15
|
-
const manuscriptPath = path.resolve(cwd, manuscriptArg);
|
|
16
|
-
const raw = readManuscript(manuscriptPath, manuscriptArg);
|
|
17
|
-
let state = loadCommentDocumentState(raw);
|
|
18
|
-
switch (subcommand) {
|
|
19
|
-
case "read": {
|
|
20
|
-
emitSuccess({
|
|
21
|
-
operation: "read",
|
|
22
|
-
manuscript: manuscriptPath,
|
|
23
|
-
state: serializeLoadedState(state)
|
|
24
|
-
}, outputFormat, `Read comments for ${path.relative(cwd, manuscriptPath) || manuscriptPath}.`);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
case "add": {
|
|
28
|
-
ensureMutableState(state, outputFormat);
|
|
29
|
-
const addInput = resolveAddInput(options, cwd, outputFormat);
|
|
30
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => addCommentToState(raw, state, addInput), (mutationResult) => mutationResult.comments);
|
|
31
|
-
state = result.state;
|
|
32
|
-
emitSuccess({
|
|
33
|
-
operation: "add",
|
|
34
|
-
manuscript: manuscriptPath,
|
|
35
|
-
commentId: result.meta.commentId,
|
|
36
|
-
state: serializeLoadedState(state)
|
|
37
|
-
}, outputFormat, `Added ${result.meta.commentId} to ${path.relative(cwd, manuscriptPath) || manuscriptPath}.`);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
case "reply": {
|
|
41
|
-
ensureMutableState(state, outputFormat);
|
|
42
|
-
const commentId = requireCommentId(options, outputFormat);
|
|
43
|
-
const { message, author } = resolveReplyInput(options, cwd, outputFormat);
|
|
44
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => replyToCommentInState(state, { commentId, message, author }), (mutationResult) => mutationResult.comments);
|
|
45
|
-
state = result.state;
|
|
46
|
-
emitSuccess({
|
|
47
|
-
operation: "reply",
|
|
48
|
-
manuscript: manuscriptPath,
|
|
49
|
-
commentId: result.meta.commentId,
|
|
50
|
-
state: serializeLoadedState(state)
|
|
51
|
-
}, outputFormat, `Added reply ${result.meta.commentId} in ${path.relative(cwd, manuscriptPath) || manuscriptPath}.`);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
case "set-status": {
|
|
55
|
-
ensureMutableState(state, outputFormat);
|
|
56
|
-
const commentId = requireCommentId(options, outputFormat);
|
|
57
|
-
const status = parseStatus(readString(options, "status"), outputFormat);
|
|
58
|
-
const thread = options.thread === true;
|
|
59
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => setCommentStatusInState(state, { commentId, status, thread }), (mutationResult) => mutationResult.comments);
|
|
60
|
-
state = result.state;
|
|
61
|
-
emitSuccess({
|
|
62
|
-
operation: "set-status",
|
|
63
|
-
manuscript: manuscriptPath,
|
|
64
|
-
status,
|
|
65
|
-
changedIds: result.meta.changedIds,
|
|
66
|
-
state: serializeLoadedState(state)
|
|
67
|
-
}, outputFormat, `Updated ${result.meta.changedIds.length} comment(s) to '${status}'.`);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
case "delete": {
|
|
71
|
-
ensureMutableState(state, outputFormat);
|
|
72
|
-
const commentId = requireCommentId(options, outputFormat);
|
|
73
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => deleteCommentInState(state, commentId), (mutationResult) => mutationResult.comments);
|
|
74
|
-
state = result.state;
|
|
75
|
-
emitSuccess({
|
|
76
|
-
operation: "delete",
|
|
77
|
-
manuscript: manuscriptPath,
|
|
78
|
-
removed: result.meta.removed,
|
|
79
|
-
state: serializeLoadedState(state)
|
|
80
|
-
}, outputFormat, `Deleted ${result.meta.removed} comment(s).`);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
case "clear-resolved": {
|
|
84
|
-
ensureMutableState(state, outputFormat);
|
|
85
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => clearResolvedInState(state), (mutationResult) => mutationResult.comments);
|
|
86
|
-
state = result.state;
|
|
87
|
-
emitSuccess({
|
|
88
|
-
operation: "clear-resolved",
|
|
89
|
-
manuscript: manuscriptPath,
|
|
90
|
-
removed: result.meta.removed,
|
|
91
|
-
state: serializeLoadedState(state)
|
|
92
|
-
}, outputFormat, `Cleared ${result.meta.removed} resolved comment(s).`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
case "sync-anchors": {
|
|
96
|
-
ensureMutableState(state, outputFormat);
|
|
97
|
-
const payload = readInputPayload(requireInputPath(options, outputFormat), cwd, outputFormat);
|
|
98
|
-
const syncInput = parseSyncInput(payload, outputFormat);
|
|
99
|
-
const result = runMutation(manuscriptPath, state, outputFormat, () => syncAnchorsInState(raw, state, syncInput), (mutationResult) => mutationResult.comments);
|
|
100
|
-
state = result.state;
|
|
101
|
-
emitSuccess({
|
|
102
|
-
operation: "sync-anchors",
|
|
103
|
-
manuscript: manuscriptPath,
|
|
104
|
-
updatedCount: result.meta.updatedCount,
|
|
105
|
-
deletedCount: result.meta.deletedCount,
|
|
106
|
-
state: serializeLoadedState(state)
|
|
107
|
-
}, outputFormat, `Synced anchors (${result.meta.updatedCount} updated, ${result.meta.deletedCount} deleted).`);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
default:
|
|
111
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `Unknown comments subcommand '${subcommand}'. Use: read, add, reply, set-status, delete, clear-resolved, sync-anchors.`, outputFormat);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function runMutation(manuscriptPath, state, outputFormat, run, getComments) {
|
|
115
|
-
let mutationResult;
|
|
116
|
-
try {
|
|
117
|
-
mutationResult = run();
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, message, outputFormat);
|
|
122
|
-
}
|
|
123
|
-
const nextComments = getComments(mutationResult);
|
|
124
|
-
const nextText = renderStateDocument(state, nextComments);
|
|
125
|
-
try {
|
|
126
|
-
fs.writeFileSync(manuscriptPath, nextText, "utf8");
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
130
|
-
throw new CommentsCommandError("WRITE_FAILURE", 6, `Failed to update manuscript: ${message}`, outputFormat);
|
|
131
|
-
}
|
|
132
|
-
const nextState = loadCommentDocumentState(nextText);
|
|
133
|
-
return {
|
|
134
|
-
meta: mutationResult,
|
|
135
|
-
state: nextState
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function ensureMutableState(state, outputFormat) {
|
|
139
|
-
try {
|
|
140
|
-
ensureNoParseErrors(state);
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
144
|
-
throw new CommentsCommandError("COMMENT_APPENDIX_INVALID", 5, message, outputFormat);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function readManuscript(absolutePath, originalArg) {
|
|
148
|
-
try {
|
|
149
|
-
const stat = fs.statSync(absolutePath);
|
|
150
|
-
if (!stat.isFile()) {
|
|
151
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `Manuscript file not found: ${originalArg}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `Manuscript file not found: ${originalArg}`);
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
return fs.readFileSync(absolutePath, "utf8");
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `Unable to read manuscript: ${message}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function emitSuccess(payload, outputFormat, textMessage) {
|
|
166
|
-
if (outputFormat === "json") {
|
|
167
|
-
console.log(JSON.stringify({ ok: true, ...payload }, null, 2));
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
console.log(textMessage);
|
|
171
|
-
}
|
|
172
|
-
function readString(options, key) {
|
|
173
|
-
const value = options[key];
|
|
174
|
-
return typeof value === "string" ? value : undefined;
|
|
175
|
-
}
|
|
176
|
-
function parseOutputFormat(raw) {
|
|
177
|
-
if (!raw || raw === "text") {
|
|
178
|
-
return "text";
|
|
179
|
-
}
|
|
180
|
-
if (raw === "json") {
|
|
181
|
-
return "json";
|
|
182
|
-
}
|
|
183
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Invalid --format value. Use 'text' or 'json'.");
|
|
184
|
-
}
|
|
185
|
-
function requireInputPath(options, outputFormat) {
|
|
186
|
-
const value = readString(options, "input");
|
|
187
|
-
if (!value) {
|
|
188
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "--input <path|-> is required for this command.", outputFormat);
|
|
189
|
-
}
|
|
190
|
-
return value;
|
|
191
|
-
}
|
|
192
|
-
function readInputPayload(inputPath, cwd, outputFormat) {
|
|
193
|
-
let rawJson = "";
|
|
194
|
-
try {
|
|
195
|
-
rawJson = inputPath === "-"
|
|
196
|
-
? fs.readFileSync(0, "utf8")
|
|
197
|
-
: fs.readFileSync(path.resolve(cwd, inputPath), "utf8");
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
201
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, `Unable to read input payload: ${message}`, outputFormat);
|
|
202
|
-
}
|
|
203
|
-
let parsed;
|
|
204
|
-
try {
|
|
205
|
-
parsed = JSON.parse(rawJson);
|
|
206
|
-
}
|
|
207
|
-
catch {
|
|
208
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload is not valid JSON.", outputFormat);
|
|
209
|
-
}
|
|
210
|
-
if (!isPlainObject(parsed)) {
|
|
211
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload must be a JSON object.", outputFormat);
|
|
212
|
-
}
|
|
213
|
-
return parsed;
|
|
214
|
-
}
|
|
215
|
-
function resolveAddInput(options, cwd, outputFormat) {
|
|
216
|
-
const messageOption = readString(options, "message");
|
|
217
|
-
const inputOption = readString(options, "input");
|
|
218
|
-
if (messageOption && inputOption) {
|
|
219
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Use exactly one payload mode: --message <text> OR --input <path|->.", outputFormat);
|
|
220
|
-
}
|
|
221
|
-
if (!messageOption && !inputOption) {
|
|
222
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Missing payload. Provide --message <text> or --input <path|->.", outputFormat);
|
|
223
|
-
}
|
|
224
|
-
const payload = inputOption ? readInputPayload(inputOption, cwd, outputFormat) : undefined;
|
|
225
|
-
const payloadMessage = coerceNonEmptyString(payload?.message, "Input payload 'message' must be a non-empty string.", outputFormat);
|
|
226
|
-
const payloadAuthor = coerceOptionalString(payload?.author, "Input payload 'author' must be a string if provided.", outputFormat);
|
|
227
|
-
const payloadMeta = payload?.meta !== undefined
|
|
228
|
-
? coerceOptionalObject(payload.meta, "Input payload 'meta' must be an object if provided.", outputFormat)
|
|
229
|
-
: undefined;
|
|
230
|
-
const optionRange = parseRangeFromOptions(options, outputFormat);
|
|
231
|
-
const optionCursorLine = parseCursorLineOption(readString(options, "cursor-line"), outputFormat);
|
|
232
|
-
const payloadRange = payload?.range !== undefined
|
|
233
|
-
? parsePayloadRange(payload.range, outputFormat)
|
|
234
|
-
: undefined;
|
|
235
|
-
const payloadAnchor = payload?.anchor !== undefined
|
|
236
|
-
? parseAnchorPayload(payload.anchor, outputFormat)
|
|
237
|
-
: undefined;
|
|
238
|
-
const finalMessage = (messageOption ?? payloadMessage ?? "").trim();
|
|
239
|
-
if (!finalMessage) {
|
|
240
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Comment message cannot be empty.", outputFormat);
|
|
241
|
-
}
|
|
242
|
-
const finalAuthor = (readString(options, "author") ?? payloadAuthor ?? "Saurus").trim() || "Saurus";
|
|
243
|
-
const anchor = {
|
|
244
|
-
range: optionRange ?? payloadAnchor?.range ?? payloadRange,
|
|
245
|
-
cursorLine: optionCursorLine ?? payloadAnchor?.cursorLine,
|
|
246
|
-
excerpt: payloadAnchor?.excerpt
|
|
247
|
-
};
|
|
248
|
-
const resolvedAnchor = anchor.range || anchor.cursorLine !== undefined || anchor.excerpt
|
|
249
|
-
? anchor
|
|
250
|
-
: undefined;
|
|
251
|
-
return {
|
|
252
|
-
message: finalMessage,
|
|
253
|
-
author: finalAuthor,
|
|
254
|
-
anchor: resolvedAnchor,
|
|
255
|
-
meta: payloadMeta
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
function resolveReplyInput(options, cwd, outputFormat) {
|
|
259
|
-
const messageOption = readString(options, "message");
|
|
260
|
-
const inputOption = readString(options, "input");
|
|
261
|
-
if (messageOption && inputOption) {
|
|
262
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Use exactly one payload mode: --message <text> OR --input <path|->.", outputFormat);
|
|
263
|
-
}
|
|
264
|
-
if (!messageOption && !inputOption) {
|
|
265
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Missing payload. Provide --message <text> or --input <path|->.", outputFormat);
|
|
266
|
-
}
|
|
267
|
-
const payload = inputOption ? readInputPayload(inputOption, cwd, outputFormat) : undefined;
|
|
268
|
-
const payloadMessage = coerceNonEmptyString(payload?.message, "Input payload 'message' must be a non-empty string.", outputFormat);
|
|
269
|
-
const payloadAuthor = coerceOptionalString(payload?.author, "Input payload 'author' must be a string if provided.", outputFormat);
|
|
270
|
-
const finalMessage = (messageOption ?? payloadMessage ?? "").trim();
|
|
271
|
-
if (!finalMessage) {
|
|
272
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Reply cannot be empty.", outputFormat);
|
|
273
|
-
}
|
|
274
|
-
const finalAuthor = (readString(options, "author") ?? payloadAuthor ?? "Saurus").trim() || "Saurus";
|
|
275
|
-
return {
|
|
276
|
-
message: finalMessage,
|
|
277
|
-
author: finalAuthor
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
function parseSyncInput(payload, outputFormat) {
|
|
281
|
-
const candidate = payload;
|
|
282
|
-
const updates = candidate.updates === undefined
|
|
283
|
-
? []
|
|
284
|
-
: parseAnchorUpdates(candidate.updates, outputFormat);
|
|
285
|
-
const deleteIdsRaw = candidate.delete_ids ?? candidate.deleteIds;
|
|
286
|
-
const deleteIds = deleteIdsRaw === undefined
|
|
287
|
-
? []
|
|
288
|
-
: parseDeleteIds(deleteIdsRaw, outputFormat);
|
|
289
|
-
return {
|
|
290
|
-
updates,
|
|
291
|
-
deleteIds
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
function parseAnchorUpdates(raw, outputFormat) {
|
|
295
|
-
if (!Array.isArray(raw)) {
|
|
296
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload 'updates' must be an array.", outputFormat);
|
|
297
|
-
}
|
|
298
|
-
const updates = [];
|
|
299
|
-
for (const candidate of raw) {
|
|
300
|
-
if (!isPlainObject(candidate)) {
|
|
301
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Each update must be an object.", outputFormat);
|
|
302
|
-
}
|
|
303
|
-
const id = typeof candidate.id === "string" ? candidate.id.trim().toUpperCase() : "";
|
|
304
|
-
if (!/^CMT-\d{4,}$/.test(id)) {
|
|
305
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Each update.id must be a comment id like CMT-0001.", outputFormat);
|
|
306
|
-
}
|
|
307
|
-
const start = isPlainObject(candidate.start) ? candidate.start : undefined;
|
|
308
|
-
const end = isPlainObject(candidate.end) ? candidate.end : undefined;
|
|
309
|
-
if (!start || !end) {
|
|
310
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Each update must include start and end positions.", outputFormat);
|
|
311
|
-
}
|
|
312
|
-
const parsed = {
|
|
313
|
-
id,
|
|
314
|
-
start: {
|
|
315
|
-
line: parseJsonPositiveInt(start.line, "updates[].start.line", outputFormat),
|
|
316
|
-
col: parseJsonNonNegativeInt(start.col, "updates[].start.col", outputFormat)
|
|
317
|
-
},
|
|
318
|
-
end: {
|
|
319
|
-
line: parseJsonPositiveInt(end.line, "updates[].end.line", outputFormat),
|
|
320
|
-
col: parseJsonNonNegativeInt(end.col, "updates[].end.col", outputFormat)
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
const valid = parsed.start.line < parsed.end.line
|
|
324
|
-
|| (parsed.start.line === parsed.end.line && parsed.start.col < parsed.end.col);
|
|
325
|
-
if (!valid) {
|
|
326
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Each update range end must be after start.", outputFormat);
|
|
327
|
-
}
|
|
328
|
-
updates.push(parsed);
|
|
329
|
-
}
|
|
330
|
-
return updates;
|
|
331
|
-
}
|
|
332
|
-
function parseDeleteIds(raw, outputFormat) {
|
|
333
|
-
if (!Array.isArray(raw)) {
|
|
334
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload 'delete_ids' must be an array.", outputFormat);
|
|
335
|
-
}
|
|
336
|
-
const ids = [];
|
|
337
|
-
for (const value of raw) {
|
|
338
|
-
if (typeof value !== "string") {
|
|
339
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "delete_ids values must be strings.", outputFormat);
|
|
340
|
-
}
|
|
341
|
-
const normalized = value.trim().toUpperCase();
|
|
342
|
-
if (!/^CMT-\d{4,}$/.test(normalized)) {
|
|
343
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, `Invalid delete_ids entry '${value}'.`, outputFormat);
|
|
344
|
-
}
|
|
345
|
-
ids.push(normalized);
|
|
346
|
-
}
|
|
347
|
-
return ids;
|
|
348
|
-
}
|
|
349
|
-
function parseAnchorPayload(raw, outputFormat) {
|
|
350
|
-
if (!isPlainObject(raw)) {
|
|
351
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload 'anchor' must be an object.", outputFormat);
|
|
352
|
-
}
|
|
353
|
-
const parsed = raw;
|
|
354
|
-
const range = parsed.range !== undefined
|
|
355
|
-
? parsePayloadRange(parsed.range, outputFormat)
|
|
356
|
-
: undefined;
|
|
357
|
-
const cursorLine = parsed.cursor_line !== undefined
|
|
358
|
-
? parseJsonPositiveInt(parsed.cursor_line, "anchor.cursor_line", outputFormat)
|
|
359
|
-
: undefined;
|
|
360
|
-
const excerpt = parsed.excerpt !== undefined
|
|
361
|
-
? coerceOptionalString(parsed.excerpt, "Input payload 'anchor.excerpt' must be a string if provided.", outputFormat)
|
|
362
|
-
: undefined;
|
|
363
|
-
return {
|
|
364
|
-
range,
|
|
365
|
-
cursorLine,
|
|
366
|
-
excerpt
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function parseRangeFromOptions(options, outputFormat) {
|
|
370
|
-
const rawStartLine = readString(options, "start-line");
|
|
371
|
-
const rawStartCol = readString(options, "start-col");
|
|
372
|
-
const rawEndLine = readString(options, "end-line");
|
|
373
|
-
const rawEndCol = readString(options, "end-col");
|
|
374
|
-
const values = [rawStartLine, rawStartCol, rawEndLine, rawEndCol];
|
|
375
|
-
const hasAny = values.some((value) => value !== undefined);
|
|
376
|
-
const hasAll = values.every((value) => value !== undefined);
|
|
377
|
-
if (!hasAny) {
|
|
378
|
-
return undefined;
|
|
379
|
-
}
|
|
380
|
-
if (!hasAll) {
|
|
381
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "Range options must include all of --start-line, --start-col, --end-line, --end-col.", outputFormat);
|
|
382
|
-
}
|
|
383
|
-
const range = {
|
|
384
|
-
startLine: parsePositiveInt(rawStartLine, "--start-line", outputFormat),
|
|
385
|
-
startCol: parseNonNegativeInt(rawStartCol, "--start-col", outputFormat),
|
|
386
|
-
endLine: parsePositiveInt(rawEndLine, "--end-line", outputFormat),
|
|
387
|
-
endCol: parseNonNegativeInt(rawEndCol, "--end-col", outputFormat)
|
|
388
|
-
};
|
|
389
|
-
validateRange(range, outputFormat);
|
|
390
|
-
return range;
|
|
391
|
-
}
|
|
392
|
-
function parseCursorLineOption(raw, outputFormat) {
|
|
393
|
-
if (raw === undefined) {
|
|
394
|
-
return undefined;
|
|
395
|
-
}
|
|
396
|
-
return parsePositiveInt(raw, "--cursor-line", outputFormat);
|
|
397
|
-
}
|
|
398
|
-
function parsePayloadRange(raw, outputFormat) {
|
|
399
|
-
if (!isPlainObject(raw)) {
|
|
400
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload range must be an object.", outputFormat);
|
|
401
|
-
}
|
|
402
|
-
const parsed = raw;
|
|
403
|
-
const start = parsed.start;
|
|
404
|
-
const end = parsed.end;
|
|
405
|
-
if (!isPlainObject(start) || !isPlainObject(end)) {
|
|
406
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Input payload range must include start and end objects.", outputFormat);
|
|
407
|
-
}
|
|
408
|
-
const range = {
|
|
409
|
-
startLine: parseJsonPositiveInt(start.line, "range.start.line", outputFormat),
|
|
410
|
-
startCol: parseJsonNonNegativeInt(start.col, "range.start.col", outputFormat),
|
|
411
|
-
endLine: parseJsonPositiveInt(end.line, "range.end.line", outputFormat),
|
|
412
|
-
endCol: parseJsonNonNegativeInt(end.col, "range.end.col", outputFormat)
|
|
413
|
-
};
|
|
414
|
-
validateRange(range, outputFormat);
|
|
415
|
-
return range;
|
|
416
|
-
}
|
|
417
|
-
function requireCommentId(options, outputFormat) {
|
|
418
|
-
const commentId = readString(options, "comment-id")?.trim().toUpperCase();
|
|
419
|
-
if (!commentId || !/^CMT-\d{4,}$/.test(commentId)) {
|
|
420
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "--comment-id CMT-#### is required.", outputFormat);
|
|
421
|
-
}
|
|
422
|
-
return commentId;
|
|
423
|
-
}
|
|
424
|
-
function parseStatus(raw, outputFormat) {
|
|
425
|
-
const normalized = (raw ?? "").trim().toLowerCase();
|
|
426
|
-
if (normalized === "open" || normalized === "resolved") {
|
|
427
|
-
return normalized;
|
|
428
|
-
}
|
|
429
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, "--status must be 'open' or 'resolved'.", outputFormat);
|
|
430
|
-
}
|
|
431
|
-
function validateRange(range, outputFormat) {
|
|
432
|
-
const startsBeforeEnd = range.startLine < range.endLine
|
|
433
|
-
|| (range.startLine === range.endLine && range.startCol < range.endCol);
|
|
434
|
-
if (!startsBeforeEnd) {
|
|
435
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, "Range end must be after range start.", outputFormat);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
function parsePositiveInt(raw, label, outputFormat) {
|
|
439
|
-
if (!/^\d+$/.test(raw)) {
|
|
440
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `${label} must be a positive integer.`, outputFormat);
|
|
441
|
-
}
|
|
442
|
-
const value = Number.parseInt(raw, 10);
|
|
443
|
-
if (!Number.isFinite(value) || value < 1) {
|
|
444
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `${label} must be a positive integer.`, outputFormat);
|
|
445
|
-
}
|
|
446
|
-
return value;
|
|
447
|
-
}
|
|
448
|
-
function parseNonNegativeInt(raw, label, outputFormat) {
|
|
449
|
-
if (!/^\d+$/.test(raw)) {
|
|
450
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `${label} must be a non-negative integer.`, outputFormat);
|
|
451
|
-
}
|
|
452
|
-
const value = Number.parseInt(raw, 10);
|
|
453
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
454
|
-
throw new CommentsCommandError("INVALID_USAGE", 2, `${label} must be a non-negative integer.`, outputFormat);
|
|
455
|
-
}
|
|
456
|
-
return value;
|
|
457
|
-
}
|
|
458
|
-
function parseJsonPositiveInt(raw, label, outputFormat) {
|
|
459
|
-
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1) {
|
|
460
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, `${label} must be a positive integer.`, outputFormat);
|
|
461
|
-
}
|
|
462
|
-
return raw;
|
|
463
|
-
}
|
|
464
|
-
function parseJsonNonNegativeInt(raw, label, outputFormat) {
|
|
465
|
-
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 0) {
|
|
466
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, `${label} must be a non-negative integer.`, outputFormat);
|
|
467
|
-
}
|
|
468
|
-
return raw;
|
|
469
|
-
}
|
|
470
|
-
function coerceNonEmptyString(value, message, outputFormat) {
|
|
471
|
-
if (value === undefined) {
|
|
472
|
-
return undefined;
|
|
473
|
-
}
|
|
474
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
475
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, message, outputFormat);
|
|
476
|
-
}
|
|
477
|
-
return value;
|
|
478
|
-
}
|
|
479
|
-
function coerceOptionalString(value, message, outputFormat) {
|
|
480
|
-
if (value === undefined) {
|
|
481
|
-
return undefined;
|
|
482
|
-
}
|
|
483
|
-
if (typeof value !== "string") {
|
|
484
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, message, outputFormat);
|
|
485
|
-
}
|
|
486
|
-
return value;
|
|
487
|
-
}
|
|
488
|
-
function coerceOptionalObject(value, message, outputFormat) {
|
|
489
|
-
if (value === undefined) {
|
|
490
|
-
return undefined;
|
|
491
|
-
}
|
|
492
|
-
if (!isPlainObject(value)) {
|
|
493
|
-
throw new CommentsCommandError("INVALID_PAYLOAD", 4, message, outputFormat);
|
|
494
|
-
}
|
|
495
|
-
return value;
|
|
496
|
-
}
|
|
497
|
-
function isPlainObject(value) {
|
|
498
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
499
|
-
}
|
package/dist/comments/errors.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** Error raised for machine-facing comments command failures. */
|
|
2
|
-
export class CommentsCommandError extends Error {
|
|
3
|
-
code;
|
|
4
|
-
exitCode;
|
|
5
|
-
outputFormat;
|
|
6
|
-
constructor(code, exitCode, message, outputFormat = "text") {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = "CommentsCommandError";
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.exitCode = exitCode;
|
|
11
|
-
this.outputFormat = outputFormat;
|
|
12
|
-
}
|
|
13
|
-
toJson() {
|
|
14
|
-
return {
|
|
15
|
-
ok: false,
|
|
16
|
-
code: this.code,
|
|
17
|
-
message: this.message
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
import { normalizeFrontmatterRecord, parseMarkdownDocument, serializeMarkdownDocument } from "./metadata-domain.js";
|
|
5
|
-
export async function runMetadataCommand(options, cwd) {
|
|
6
|
-
const [subcommand, markdownArg] = options._;
|
|
7
|
-
if (!subcommand) {
|
|
8
|
-
throw new Error("Metadata subcommand is required. Use: read, apply.");
|
|
9
|
-
}
|
|
10
|
-
if (!markdownArg) {
|
|
11
|
-
throw new Error("Markdown path is required. Use: stego metadata <subcommand> <path>.");
|
|
12
|
-
}
|
|
13
|
-
const outputFormat = parseOutputFormat(readString(options, "format"));
|
|
14
|
-
const absolutePath = path.resolve(cwd, markdownArg);
|
|
15
|
-
const raw = readFile(absolutePath, markdownArg);
|
|
16
|
-
switch (subcommand) {
|
|
17
|
-
case "read": {
|
|
18
|
-
const parsed = parseMarkdownDocument(raw);
|
|
19
|
-
emit({
|
|
20
|
-
ok: true,
|
|
21
|
-
operation: "read",
|
|
22
|
-
state: {
|
|
23
|
-
path: absolutePath,
|
|
24
|
-
hasFrontmatter: parsed.hasFrontmatter,
|
|
25
|
-
lineEnding: parsed.lineEnding,
|
|
26
|
-
frontmatter: parsed.frontmatter,
|
|
27
|
-
body: parsed.body
|
|
28
|
-
}
|
|
29
|
-
}, outputFormat);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
case "apply": {
|
|
33
|
-
const inputPath = requireInputPath(options);
|
|
34
|
-
const payload = readInputPayload(inputPath, cwd);
|
|
35
|
-
const frontmatter = normalizeFrontmatterRecord(payload.frontmatter);
|
|
36
|
-
const body = typeof payload.body === "string" ? payload.body : undefined;
|
|
37
|
-
const hasFrontmatter = typeof payload.hasFrontmatter === "boolean" ? payload.hasFrontmatter : undefined;
|
|
38
|
-
const existing = parseMarkdownDocument(raw);
|
|
39
|
-
const next = {
|
|
40
|
-
lineEnding: existing.lineEnding,
|
|
41
|
-
hasFrontmatter: hasFrontmatter ?? (existing.hasFrontmatter || Object.keys(frontmatter).length > 0),
|
|
42
|
-
frontmatter,
|
|
43
|
-
body: body ?? existing.body
|
|
44
|
-
};
|
|
45
|
-
const nextText = serializeMarkdownDocument(next);
|
|
46
|
-
const changed = nextText !== raw;
|
|
47
|
-
if (changed) {
|
|
48
|
-
fs.writeFileSync(absolutePath, nextText, "utf8");
|
|
49
|
-
}
|
|
50
|
-
const reparsed = parseMarkdownDocument(nextText);
|
|
51
|
-
emit({
|
|
52
|
-
ok: true,
|
|
53
|
-
operation: "apply",
|
|
54
|
-
changed,
|
|
55
|
-
state: {
|
|
56
|
-
path: absolutePath,
|
|
57
|
-
hasFrontmatter: reparsed.hasFrontmatter,
|
|
58
|
-
lineEnding: reparsed.lineEnding,
|
|
59
|
-
frontmatter: reparsed.frontmatter,
|
|
60
|
-
body: reparsed.body
|
|
61
|
-
}
|
|
62
|
-
}, outputFormat);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
default:
|
|
66
|
-
throw new Error(`Unknown metadata subcommand '${subcommand}'. Use: read, apply.`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function emit(payload, format) {
|
|
70
|
-
if (format === "json") {
|
|
71
|
-
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (payload.operation === "read") {
|
|
75
|
-
process.stdout.write(`Read metadata for ${payload.state.path} (${Object.keys(payload.state.frontmatter).length} keys).\n`);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
process.stdout.write(`${payload.changed ? "Updated" : "No changes for"} metadata in ${payload.state.path}.\n`);
|
|
79
|
-
}
|
|
80
|
-
function parseOutputFormat(raw) {
|
|
81
|
-
if (!raw || raw === "text") {
|
|
82
|
-
return "text";
|
|
83
|
-
}
|
|
84
|
-
if (raw === "json") {
|
|
85
|
-
return "json";
|
|
86
|
-
}
|
|
87
|
-
throw new Error("Invalid --format value. Use 'text' or 'json'.");
|
|
88
|
-
}
|
|
89
|
-
function readString(options, key) {
|
|
90
|
-
const value = options[key];
|
|
91
|
-
return typeof value === "string" ? value : undefined;
|
|
92
|
-
}
|
|
93
|
-
function requireInputPath(options) {
|
|
94
|
-
const inputPath = readString(options, "input");
|
|
95
|
-
if (!inputPath) {
|
|
96
|
-
throw new Error("--input <path|-> is required for 'metadata apply'.");
|
|
97
|
-
}
|
|
98
|
-
return inputPath;
|
|
99
|
-
}
|
|
100
|
-
function readInputPayload(inputPath, cwd) {
|
|
101
|
-
const raw = inputPath === "-"
|
|
102
|
-
? fs.readFileSync(process.stdin.fd, "utf8")
|
|
103
|
-
: fs.readFileSync(path.resolve(cwd, inputPath), "utf8");
|
|
104
|
-
let parsed;
|
|
105
|
-
try {
|
|
106
|
-
parsed = JSON.parse(raw);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
throw new Error("Input payload is not valid JSON.");
|
|
110
|
-
}
|
|
111
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
112
|
-
throw new Error("Input payload must be a JSON object.");
|
|
113
|
-
}
|
|
114
|
-
return parsed;
|
|
115
|
-
}
|
|
116
|
-
function readFile(filePath, originalArg) {
|
|
117
|
-
try {
|
|
118
|
-
const stat = fs.statSync(filePath);
|
|
119
|
-
if (!stat.isFile()) {
|
|
120
|
-
throw new Error();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
throw new Error(`Markdown file not found: ${originalArg}`);
|
|
125
|
-
}
|
|
126
|
-
return fs.readFileSync(filePath, "utf8");
|
|
127
|
-
}
|