@vecteur/cli 0.8.0 → 0.9.1

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,226 @@
1
+ // GENERATED from frontend/src/run/markdown.ts; no runtime frontend dependency.
2
+ export function inlinePlain(inline) {
3
+ return inline.map((token) => token.text).join('');
4
+ }
5
+ export function tableGrid(block) {
6
+ const header = block.header.map(inlinePlain);
7
+ const rows = block.rows.map((row) => row.map(inlinePlain));
8
+ return header.length ? [header, ...rows] : rows;
9
+ }
10
+ const INLINE_PATTERN = /(!\[[^\]]*\]\([^)\s]+\)|`[^`]+`|\[[^\]\n]+\]\([^)\s]+\)|\*\*[^*]+\*\*|\*[^*]+\*|\$[A-Za-z_][A-Za-z0-9_.]*\$)/g;
11
+ const LINK_TOKEN = /^\[([^\]\n]+)\]\(([^)\s]+)\)$/;
12
+ const IMAGE_TOKEN = /^!\[([^\]]*)\]\(([^)\s]+)\)$/;
13
+ const ESCAPE_SPLIT = /\\([*`$])/g;
14
+ const TABLE_ROW = /^\s*\|.*\|\s*$/;
15
+ const QUOTE_ROW = /^>\s?/;
16
+ const BULLET_ROW = /^\s*[-*]\s+(.*)$/;
17
+ const NUMBERED_ROW = /^\s*\d+[.)]\s+(.*)$/;
18
+ const HR_ROW = /^(?:-{3,}|\*{3,}|_{3,})\s*$/;
19
+ function startsBlock(line) {
20
+ return line.startsWith('```') || /^(#{1,4})\s/.test(line)
21
+ || QUOTE_ROW.test(line) || TABLE_ROW.test(line)
22
+ || BULLET_ROW.test(line) || NUMBERED_ROW.test(line)
23
+ || HR_ROW.test(line);
24
+ }
25
+ function escapeSegments(text) {
26
+ const segments = [];
27
+ let last = 0;
28
+ for (const match of text.matchAll(ESCAPE_SPLIT)) {
29
+ const index = match.index ?? 0;
30
+ if (index > last)
31
+ segments.push({ literal: false, text: text.slice(last, index) });
32
+ segments.push({ literal: true, text: match[1] });
33
+ last = index + match[0].length;
34
+ }
35
+ if (last < text.length)
36
+ segments.push({ literal: false, text: text.slice(last) });
37
+ return segments;
38
+ }
39
+ function pushText(out, text) {
40
+ if (!text)
41
+ return;
42
+ const tail = out[out.length - 1];
43
+ if (tail && tail.kind === 'text')
44
+ tail.text += text;
45
+ else
46
+ out.push({ kind: 'text', text });
47
+ }
48
+ export function parseInline(text) {
49
+ const out = [];
50
+ for (const segment of escapeSegments(text)) {
51
+ if (segment.literal) {
52
+ pushText(out, segment.text);
53
+ continue;
54
+ }
55
+ let last = 0;
56
+ for (const match of segment.text.matchAll(INLINE_PATTERN)) {
57
+ const index = match.index ?? 0;
58
+ if (index > last)
59
+ pushText(out, segment.text.slice(last, index));
60
+ const token = match[0];
61
+ const image = token.startsWith('![') ? IMAGE_TOKEN.exec(token) : null;
62
+ const link = !image && token.startsWith('[') ? LINK_TOKEN.exec(token) : null;
63
+ if (token.startsWith('`'))
64
+ out.push({ kind: 'code', text: token.slice(1, -1) });
65
+ else if (image)
66
+ out.push({ kind: 'image', text: image[1], href: image[2] });
67
+ else if (link)
68
+ out.push({ kind: 'link', text: link[1], href: link[2] });
69
+ else if (token.startsWith('**'))
70
+ out.push({ kind: 'strong', text: token.slice(2, -2) });
71
+ else if (token.startsWith('$'))
72
+ out.push({ kind: 'code', text: token.slice(1, -1) });
73
+ else
74
+ out.push({ kind: 'em', text: token.slice(1, -1) });
75
+ last = index + token.length;
76
+ }
77
+ if (last < segment.text.length)
78
+ pushText(out, segment.text.slice(last));
79
+ }
80
+ return out;
81
+ }
82
+ export function withholdOpenTokens(text) {
83
+ const scrubbed = text.replace(/\\[*`$]/g, ' ');
84
+ const cuts = [];
85
+ const backticks = [...scrubbed.matchAll(/`/g)];
86
+ if (backticks.length % 2 === 1)
87
+ cuts.push(backticks[backticks.length - 1].index ?? 0);
88
+ const bolds = [...scrubbed.matchAll(/\*\*/g)];
89
+ if (bolds.length % 2 === 1)
90
+ cuts.push(bolds[bolds.length - 1].index ?? 0);
91
+ const openReference = /\$[A-Za-z_][A-Za-z0-9_.]*$/.exec(scrubbed);
92
+ if (openReference)
93
+ cuts.push(openReference.index);
94
+ const openLink = /\[[^\]\n]*\]\([^)\s]*$/.exec(scrubbed);
95
+ if (openLink)
96
+ cuts.push(openLink.index);
97
+ const tablePrefix = trailingTableDelimiterPrefix(text);
98
+ if (tablePrefix !== null)
99
+ cuts.push(tablePrefix);
100
+ if (!cuts.length)
101
+ return text;
102
+ return text.slice(0, Math.min(...cuts));
103
+ }
104
+ function trailingTableDelimiterPrefix(text) {
105
+ const lines = text.split('\n');
106
+ if (lines.length < 2)
107
+ return null;
108
+ const candidate = lines[lines.length - 1];
109
+ const header = lines[lines.length - 2];
110
+ if (!TABLE_ROW.test(candidate) || !TABLE_ROW.test(header))
111
+ return null;
112
+ if (lines.slice(0, -1).filter((line) => line.startsWith('```')).length % 2 === 1)
113
+ return null;
114
+ if (lines.length > 2 && TABLE_ROW.test(lines[lines.length - 3]))
115
+ return null;
116
+ const cells = (line) => line.trim().replace(/^\||\|$/g, '').split('|').map((cell) => cell.trim());
117
+ const delimiter = cells(candidate);
118
+ const headerCells = cells(header);
119
+ if (delimiter.length >= headerCells.length)
120
+ return null;
121
+ if (!delimiter.every((cell) => /^:?-+:?$/.test(cell)))
122
+ return null;
123
+ return text.lastIndexOf('\n') + 1;
124
+ }
125
+ export function splitStream(source) {
126
+ const lines = source.split('\n');
127
+ let fenceOpen = -1;
128
+ let offset = 0;
129
+ for (const line of lines) {
130
+ if (line.startsWith('```'))
131
+ fenceOpen = fenceOpen === -1 ? offset : -1;
132
+ offset += line.length + 1;
133
+ }
134
+ if (fenceOpen !== -1) {
135
+ return { frozen: source.slice(0, fenceOpen), tail: source.slice(fenceOpen) };
136
+ }
137
+ const boundary = source.lastIndexOf('\n\n');
138
+ if (boundary === -1)
139
+ return { frozen: '', tail: source };
140
+ return { frozen: source.slice(0, boundary + 2), tail: source.slice(boundary + 2) };
141
+ }
142
+ export function parseMarkdown(source) {
143
+ const blocks = [];
144
+ const lines = source.split('\n');
145
+ let index = 0;
146
+ while (index < lines.length) {
147
+ const line = lines[index];
148
+ if (line.trim() === '') {
149
+ index += 1;
150
+ continue;
151
+ }
152
+ if (line.startsWith('```')) {
153
+ const language = line.slice(3).trim();
154
+ const body = [];
155
+ index += 1;
156
+ while (index < lines.length && !lines[index].startsWith('```')) {
157
+ body.push(lines[index]);
158
+ index += 1;
159
+ }
160
+ index += 1;
161
+ blocks.push({ kind: 'codeblock', text: body.join('\n'), language });
162
+ continue;
163
+ }
164
+ const heading = /^(#{1,4})\s+(.*)$/.exec(line);
165
+ if (heading) {
166
+ blocks.push({ kind: 'heading', level: heading[1].length, inline: parseInline(heading[2]) });
167
+ index += 1;
168
+ continue;
169
+ }
170
+ if (HR_ROW.test(line)) {
171
+ blocks.push({ kind: 'hr' });
172
+ index += 1;
173
+ continue;
174
+ }
175
+ if (QUOTE_ROW.test(line)) {
176
+ const body = [];
177
+ while (index < lines.length && QUOTE_ROW.test(lines[index])) {
178
+ body.push(lines[index].replace(QUOTE_ROW, ''));
179
+ index += 1;
180
+ }
181
+ blocks.push({ kind: 'quote', inline: parseInline(body.join(' ').trim()) });
182
+ continue;
183
+ }
184
+ if (TABLE_ROW.test(line)) {
185
+ const rows = [];
186
+ while (index < lines.length && TABLE_ROW.test(lines[index])) {
187
+ rows.push(lines[index].trim().replace(/^\||\|$/g, '').split('|').map((cell) => cell.trim()));
188
+ index += 1;
189
+ }
190
+ const isRule = (cells) => cells.every((cell) => /^:?-+:?$/.test(cell));
191
+ const header = rows.length > 1 && rows[0].length === rows[1].length && isRule(rows[1])
192
+ ? rows[0]
193
+ : null;
194
+ const body = header ? rows.slice(2) : rows;
195
+ blocks.push({
196
+ kind: 'table',
197
+ header: (header ?? []).map(parseInline),
198
+ rows: body.map((cells) => cells.map(parseInline)),
199
+ });
200
+ continue;
201
+ }
202
+ if (BULLET_ROW.test(line) || NUMBERED_ROW.test(line)) {
203
+ const ordered = NUMBERED_ROW.test(line);
204
+ const pattern = ordered ? NUMBERED_ROW : BULLET_ROW;
205
+ const items = [];
206
+ while (index < lines.length) {
207
+ const item = pattern.exec(lines[index]);
208
+ if (!item)
209
+ break;
210
+ items.push(parseInline(item[1]));
211
+ index += 1;
212
+ }
213
+ blocks.push({ kind: 'list', ordered, items });
214
+ continue;
215
+ }
216
+ const paragraph = [line];
217
+ index += 1;
218
+ while (index < lines.length && lines[index].trim() !== ''
219
+ && !startsBlock(lines[index])) {
220
+ paragraph.push(lines[index]);
221
+ index += 1;
222
+ }
223
+ blocks.push({ kind: 'paragraph', inline: parseInline(paragraph.join('\n')) });
224
+ }
225
+ return blocks;
226
+ }
@@ -0,0 +1,29 @@
1
+ import { dirname, join } from "node:path";
2
+ import { credentialsPath } from "./credentials.js";
3
+ import { readStore, writeStore, withStoreLock } from "./store.js";
4
+ import { CliError } from "./errors.js";
5
+
6
+ const defaults = { plain: false, reducedMotion: false, updates: {} };
7
+ const path = env => join(dirname(credentialsPath(env)), "preferences.json");
8
+ export function preferences(env = process.env) {
9
+ const value = readStore(path(env));
10
+ if (value === null) return structuredClone(defaults);
11
+ if (value.version !== 1 || typeof value.plain !== "boolean" || typeof value.reducedMotion !== "boolean"
12
+ || !value.updates || typeof value.updates !== "object" || Array.isArray(value.updates)
13
+ || Object.keys(value).sort().join() !== "plain,reducedMotion,updates,version"
14
+ || Object.values(value.updates).some(mode => !["ask", "auto", "off"].includes(mode))) {
15
+ throw new CliError("preferences_version", "Unsupported preferences; existing settings preserved. Use --plain for this session.");
16
+ }
17
+ return value;
18
+ }
19
+ export function savePreferences(patch, env = process.env) {
20
+ return withStoreLock(path(env), () => {
21
+ const value = preferences(env);
22
+ for (const key of Object.keys(patch)) if (!Object.hasOwn(defaults, key)) throw new CliError("preferences_invalid", "Unknown preference");
23
+ const next = { ...value, ...patch, version: 1 };
24
+ if (typeof next.plain !== "boolean" || typeof next.reducedMotion !== "boolean"
25
+ || !next.updates || typeof next.updates !== "object" || Array.isArray(next.updates)
26
+ || Object.values(next.updates).some(mode => !["ask", "auto", "off"].includes(mode))) throw new CliError("preferences_invalid", "Invalid preference");
27
+ writeStore(path(env), next); return next;
28
+ });
29
+ }