@descent-vtt/spec-brief 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/bin/spec-brief.js +19 -0
- package/dist/apply.d.ts +26 -0
- package/dist/apply.js +71 -0
- package/dist/apply.js.map +1 -0
- package/dist/archive.d.ts +81 -0
- package/dist/archive.js +333 -0
- package/dist/archive.js.map +1 -0
- package/dist/brief.d.ts +60 -0
- package/dist/brief.js +152 -0
- package/dist/brief.js.map +1 -0
- package/dist/cli.d.ts +35 -0
- package/dist/cli.js +411 -0
- package/dist/cli.js.map +1 -0
- package/dist/collisions.d.ts +50 -0
- package/dist/collisions.js +127 -0
- package/dist/collisions.js.map +1 -0
- package/dist/config.d.ts +94 -0
- package/dist/config.js +353 -0
- package/dist/config.js.map +1 -0
- package/dist/corpus.d.ts +41 -0
- package/dist/corpus.js +154 -0
- package/dist/corpus.js.map +1 -0
- package/dist/engine.d.ts +121 -0
- package/dist/engine.js +276 -0
- package/dist/engine.js.map +1 -0
- package/dist/frontmatter.d.ts +68 -0
- package/dist/frontmatter.js +311 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/fs.d.ts +59 -0
- package/dist/fs.js +189 -0
- package/dist/fs.js.map +1 -0
- package/dist/git.d.ts +59 -0
- package/dist/git.js +131 -0
- package/dist/git.js.map +1 -0
- package/dist/glob.d.ts +79 -0
- package/dist/glob.js +465 -0
- package/dist/glob.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/integrity.d.ts +11 -0
- package/dist/integrity.js +20 -0
- package/dist/integrity.js.map +1 -0
- package/dist/links.d.ts +38 -0
- package/dist/links.js +142 -0
- package/dist/links.js.map +1 -0
- package/dist/lint.d.ts +38 -0
- package/dist/lint.js +90 -0
- package/dist/lint.js.map +1 -0
- package/dist/markdown.d.ts +65 -0
- package/dist/markdown.js +274 -0
- package/dist/markdown.js.map +1 -0
- package/dist/plugins.d.ts +16 -0
- package/dist/plugins.js +77 -0
- package/dist/plugins.js.map +1 -0
- package/dist/report.d.ts +38 -0
- package/dist/report.js +244 -0
- package/dist/report.js.map +1 -0
- package/dist/rules.d.ts +58 -0
- package/dist/rules.js +448 -0
- package/dist/rules.js.map +1 -0
- package/dist/scaffold.d.ts +25 -0
- package/dist/scaffold.js +81 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/schema.d.ts +47 -0
- package/dist/schema.js +195 -0
- package/dist/schema.js.map +1 -0
- package/dist/text.d.ts +40 -0
- package/dist/text.js +95 -0
- package/dist/text.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +76 -0
- package/schema.json +321 -0
package/dist/glob.js
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Globs: parsing, matching, and deciding whether two globs can name the same file.
|
|
3
|
+
*
|
|
4
|
+
* The dialect is the one people type: `**`, `*`, `?`, `[abc]`, `[!a-z]`,
|
|
5
|
+
* `{a,b}` and `\` to escape. A pattern with no glob syntax at all names a
|
|
6
|
+
* directory and everything beneath it, so `src/auth` covers
|
|
7
|
+
* `src/auth/login.ts` - unless it names a file, one the tree holds or one with
|
|
8
|
+
* an extension, which matches only itself. A trailing `/` always means a
|
|
9
|
+
* directory. Paths
|
|
10
|
+
* are repository-relative and POSIX, and matching is case-sensitive on every
|
|
11
|
+
* host, because git's paths are and a result should not depend on who ran it.
|
|
12
|
+
* `*` matches a leading dot; a scope that forgot its dotfiles is not a scope
|
|
13
|
+
* that excludes them.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here compiles to a `RegExp`. Matching and intersection are dynamic
|
|
16
|
+
* programmes over the pattern and the subject, so a pattern of `*a*a*a*a*b`
|
|
17
|
+
* against a long name costs the product of their lengths and cannot backtrack
|
|
18
|
+
* into an exponent. The same property is why the sibling tools match their own
|
|
19
|
+
* globs this way.
|
|
20
|
+
*/
|
|
21
|
+
/** More alternatives than this is a pattern nobody meant. */
|
|
22
|
+
export const MAX_ALTERNATIVES = 256;
|
|
23
|
+
const SLASH = 0x2f;
|
|
24
|
+
const GLOBSTAR = { kind: 'globstar' };
|
|
25
|
+
/** A name with an extension, `login.ts` or `.eslintrc.json`, and not a dot-directory such as `.github`. */
|
|
26
|
+
function hasExtension(name) {
|
|
27
|
+
return /.\.[^.]+$/.test(name);
|
|
28
|
+
}
|
|
29
|
+
export function parseGlob(source, options = {}) {
|
|
30
|
+
let pattern = source.trim();
|
|
31
|
+
if (pattern.length === 0)
|
|
32
|
+
return { ok: false, error: 'the pattern is empty' };
|
|
33
|
+
if (pattern.startsWith('!'))
|
|
34
|
+
return { ok: false, error: 'negated patterns are not supported; narrow the positive pattern' };
|
|
35
|
+
if (pattern.startsWith('/'))
|
|
36
|
+
return { ok: false, error: 'a pattern is relative to the repository root and cannot start with "/"' };
|
|
37
|
+
if (/(?:^|[^\\])[@+!?*]\(/.test(pattern))
|
|
38
|
+
return { ok: false, error: 'extended globs such as "+(a|b)" are not supported' };
|
|
39
|
+
// A leading "./" needs no stripping: "." segments are dropped below.
|
|
40
|
+
if (pattern.endsWith('/'))
|
|
41
|
+
pattern = `${pattern}**`;
|
|
42
|
+
const expanded = expandBraces(pattern);
|
|
43
|
+
if (typeof expanded === 'string')
|
|
44
|
+
return { ok: false, error: expanded };
|
|
45
|
+
const alternatives = [];
|
|
46
|
+
for (const alternative of expanded) {
|
|
47
|
+
const segments = [];
|
|
48
|
+
for (const raw of alternative.split('/')) {
|
|
49
|
+
if (raw === '' || raw === '.')
|
|
50
|
+
continue;
|
|
51
|
+
if (raw === '..')
|
|
52
|
+
return { ok: false, error: 'a pattern cannot leave the repository with ".."' };
|
|
53
|
+
if (raw === '**') {
|
|
54
|
+
if (segments[segments.length - 1]?.kind !== 'globstar')
|
|
55
|
+
segments.push(GLOBSTAR);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const tokens = tokenize(raw);
|
|
59
|
+
if (typeof tokens === 'string')
|
|
60
|
+
return { ok: false, error: tokens };
|
|
61
|
+
segments.push({ kind: 'pattern', tokens, literal: tokens.every((t) => t.kind === 'literal') });
|
|
62
|
+
}
|
|
63
|
+
if (segments.length === 0)
|
|
64
|
+
return { ok: false, error: 'the pattern names no path' };
|
|
65
|
+
// A path with no glob syntax names a directory and everything beneath it,
|
|
66
|
+
// unless it names a file: one the tree has, or one with an extension.
|
|
67
|
+
// Treating a file as a directory would let "src/a.ts" overlap
|
|
68
|
+
// "**/session.ts" through a "src/a.ts/session.ts" nobody can create.
|
|
69
|
+
if (segments.every((s) => s.kind === 'pattern' && s.literal)) {
|
|
70
|
+
const path = segments
|
|
71
|
+
.map((s) => (s.kind === 'pattern' ? s.tokens.map((t) => (t.kind === 'literal' ? t.char : '')).join('') : ''))
|
|
72
|
+
.join('/');
|
|
73
|
+
const last = path.slice(path.lastIndexOf('/') + 1);
|
|
74
|
+
if (options.isFile?.(path) !== true && !hasExtension(last))
|
|
75
|
+
segments.push(GLOBSTAR);
|
|
76
|
+
}
|
|
77
|
+
alternatives.push(segments);
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, glob: { source, alternatives } };
|
|
80
|
+
}
|
|
81
|
+
/** Expands `{a,b}` groups, innermost last, or says why the pattern is malformed. */
|
|
82
|
+
function expandBraces(pattern) {
|
|
83
|
+
let open = -1;
|
|
84
|
+
let depth = 0;
|
|
85
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
86
|
+
const ch = pattern.charAt(i);
|
|
87
|
+
if (ch === '\\') {
|
|
88
|
+
i += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (ch === '[') {
|
|
92
|
+
const close = classEnd(pattern, i);
|
|
93
|
+
if (close > 0)
|
|
94
|
+
i = close;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (ch === '{') {
|
|
98
|
+
if (depth === 0)
|
|
99
|
+
open = i;
|
|
100
|
+
depth += 1;
|
|
101
|
+
}
|
|
102
|
+
else if (ch === '}') {
|
|
103
|
+
if (depth === 0)
|
|
104
|
+
return 'a "}" closes no "{"';
|
|
105
|
+
depth -= 1;
|
|
106
|
+
if (depth === 0) {
|
|
107
|
+
const options = splitTopLevel(pattern.slice(open + 1, i));
|
|
108
|
+
const prefix = pattern.slice(0, open);
|
|
109
|
+
const suffix = pattern.slice(i + 1);
|
|
110
|
+
const results = [];
|
|
111
|
+
for (const option of options) {
|
|
112
|
+
const expanded = expandBraces(`${prefix}${option}${suffix}`);
|
|
113
|
+
if (typeof expanded === 'string')
|
|
114
|
+
return expanded;
|
|
115
|
+
results.push(...expanded);
|
|
116
|
+
if (results.length > MAX_ALTERNATIVES)
|
|
117
|
+
return `the braces expand to more than ${MAX_ALTERNATIVES} patterns`;
|
|
118
|
+
}
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (depth > 0)
|
|
124
|
+
return 'a "{" is never closed';
|
|
125
|
+
return [pattern];
|
|
126
|
+
}
|
|
127
|
+
function splitTopLevel(body) {
|
|
128
|
+
const parts = [];
|
|
129
|
+
let depth = 0;
|
|
130
|
+
let start = 0;
|
|
131
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
132
|
+
const ch = body.charAt(i);
|
|
133
|
+
if (ch === '\\')
|
|
134
|
+
i += 1;
|
|
135
|
+
else if (ch === '{')
|
|
136
|
+
depth += 1;
|
|
137
|
+
else if (ch === '}')
|
|
138
|
+
depth -= 1;
|
|
139
|
+
else if (ch === ',' && depth === 0) {
|
|
140
|
+
parts.push(body.slice(start, i));
|
|
141
|
+
start = i + 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
parts.push(body.slice(start));
|
|
145
|
+
return parts;
|
|
146
|
+
}
|
|
147
|
+
/** The index of the `]` closing a class opened at `open`, or -1. */
|
|
148
|
+
function classEnd(pattern, open) {
|
|
149
|
+
let i = open + 1;
|
|
150
|
+
if (pattern.charAt(i) === '!' || pattern.charAt(i) === '^')
|
|
151
|
+
i += 1;
|
|
152
|
+
if (pattern.charAt(i) === ']')
|
|
153
|
+
i += 1;
|
|
154
|
+
for (; i < pattern.length; i += 1) {
|
|
155
|
+
const ch = pattern.charAt(i);
|
|
156
|
+
if (ch === '/')
|
|
157
|
+
return -1;
|
|
158
|
+
if (ch === ']')
|
|
159
|
+
return i;
|
|
160
|
+
}
|
|
161
|
+
return -1;
|
|
162
|
+
}
|
|
163
|
+
function tokenize(segment) {
|
|
164
|
+
const tokens = [];
|
|
165
|
+
const chars = Array.from(segment);
|
|
166
|
+
for (let i = 0; i < chars.length; i += 1) {
|
|
167
|
+
const ch = chars[i];
|
|
168
|
+
if (ch === '\\') {
|
|
169
|
+
const next = chars[i + 1];
|
|
170
|
+
// A backslash before a letter, a digit or nothing is a Windows separator, not an escape.
|
|
171
|
+
if (next === undefined || /[A-Za-z0-9]/.test(next))
|
|
172
|
+
return '"\\" escapes glob syntax; separate directories with "/"';
|
|
173
|
+
tokens.push({ kind: 'literal', char: next });
|
|
174
|
+
i += 1;
|
|
175
|
+
}
|
|
176
|
+
else if (ch === '*') {
|
|
177
|
+
if (tokens[tokens.length - 1]?.kind !== 'star')
|
|
178
|
+
tokens.push({ kind: 'star' });
|
|
179
|
+
}
|
|
180
|
+
else if (ch === '?') {
|
|
181
|
+
tokens.push({ kind: 'any' });
|
|
182
|
+
}
|
|
183
|
+
else if (ch === '[') {
|
|
184
|
+
const parsed = readClass(chars, i);
|
|
185
|
+
if (typeof parsed === 'string')
|
|
186
|
+
return parsed;
|
|
187
|
+
tokens.push(parsed.token);
|
|
188
|
+
i = parsed.end;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
tokens.push({ kind: 'literal', char: ch });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return tokens;
|
|
195
|
+
}
|
|
196
|
+
function readClass(chars, open) {
|
|
197
|
+
let i = open + 1;
|
|
198
|
+
let negated = false;
|
|
199
|
+
if (chars[i] === '!' || chars[i] === '^') {
|
|
200
|
+
negated = true;
|
|
201
|
+
i += 1;
|
|
202
|
+
}
|
|
203
|
+
const ranges = [];
|
|
204
|
+
let first = true;
|
|
205
|
+
for (; i < chars.length; i += 1) {
|
|
206
|
+
const ch = chars[i];
|
|
207
|
+
// Never empty: the first character is always a member, even "]".
|
|
208
|
+
if (ch === ']' && !first)
|
|
209
|
+
return { token: { kind: 'class', negated, ranges }, end: i };
|
|
210
|
+
first = false;
|
|
211
|
+
let lo = ch;
|
|
212
|
+
if (ch === '\\' && chars[i + 1] !== undefined) {
|
|
213
|
+
i += 1;
|
|
214
|
+
lo = chars[i];
|
|
215
|
+
}
|
|
216
|
+
let hi = lo;
|
|
217
|
+
if (chars[i + 1] === '-' && chars[i + 2] !== undefined && chars[i + 2] !== ']') {
|
|
218
|
+
hi = chars[i + 2];
|
|
219
|
+
i += 2;
|
|
220
|
+
}
|
|
221
|
+
const from = lo.codePointAt(0);
|
|
222
|
+
const to = hi.codePointAt(0);
|
|
223
|
+
if (to < from)
|
|
224
|
+
return `the range "${lo}-${hi}" runs backwards`;
|
|
225
|
+
ranges.push([from, to]);
|
|
226
|
+
}
|
|
227
|
+
return 'a "[" is never closed';
|
|
228
|
+
}
|
|
229
|
+
function splitPath(path) {
|
|
230
|
+
return path.split('/').filter((s) => s !== '' && s !== '.');
|
|
231
|
+
}
|
|
232
|
+
/** Membership of a class. A slash is never asked about: paths are matched a segment at a time. */
|
|
233
|
+
function inClass(token, point) {
|
|
234
|
+
const inside = token.ranges.some(([lo, hi]) => point >= lo && point <= hi);
|
|
235
|
+
return token.negated ? !inside : inside;
|
|
236
|
+
}
|
|
237
|
+
function charMatches(token, char) {
|
|
238
|
+
switch (token.kind) {
|
|
239
|
+
case 'literal':
|
|
240
|
+
return token.char === char;
|
|
241
|
+
case 'any':
|
|
242
|
+
return true;
|
|
243
|
+
case 'class':
|
|
244
|
+
return inClass(token, char.codePointAt(0));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/** Whether one segment of a path matches a pattern segment's tokens. */
|
|
248
|
+
function tokensMatch(tokens, subject) {
|
|
249
|
+
const chars = Array.from(subject);
|
|
250
|
+
// next[j]: tokens[i+1..] match chars[j..]; filled from the end of both.
|
|
251
|
+
let next = new Array(chars.length + 1).fill(false);
|
|
252
|
+
next[chars.length] = true;
|
|
253
|
+
for (let i = tokens.length - 1; i >= 0; i -= 1) {
|
|
254
|
+
const token = tokens[i];
|
|
255
|
+
const row = new Array(chars.length + 1).fill(false);
|
|
256
|
+
for (let j = chars.length; j >= 0; j -= 1) {
|
|
257
|
+
row[j] =
|
|
258
|
+
token.kind === 'star'
|
|
259
|
+
? next[j] || (j < chars.length && row[j + 1])
|
|
260
|
+
: j < chars.length && charMatches(token, chars[j]) && next[j + 1];
|
|
261
|
+
}
|
|
262
|
+
next = row;
|
|
263
|
+
}
|
|
264
|
+
return next[0];
|
|
265
|
+
}
|
|
266
|
+
function segmentsMatch(segments, path) {
|
|
267
|
+
let next = new Array(path.length + 1).fill(false);
|
|
268
|
+
next[path.length] = true;
|
|
269
|
+
for (let i = segments.length - 1; i >= 0; i -= 1) {
|
|
270
|
+
const segment = segments[i];
|
|
271
|
+
const row = new Array(path.length + 1).fill(false);
|
|
272
|
+
for (let j = path.length; j >= 0; j -= 1) {
|
|
273
|
+
row[j] =
|
|
274
|
+
segment.kind === 'globstar'
|
|
275
|
+
? next[j] || (j < path.length && row[j + 1])
|
|
276
|
+
: j < path.length && tokensMatch(segment.tokens, path[j]) && next[j + 1];
|
|
277
|
+
}
|
|
278
|
+
next = row;
|
|
279
|
+
}
|
|
280
|
+
return next[0];
|
|
281
|
+
}
|
|
282
|
+
export function matchGlob(glob, path) {
|
|
283
|
+
const segments = splitPath(path);
|
|
284
|
+
if (segments.length === 0)
|
|
285
|
+
return false;
|
|
286
|
+
return glob.alternatives.some((alternative) => segmentsMatch(alternative, segments));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* A path both globs match, or `null` when there is none. The answer is exact
|
|
290
|
+
* for the dialect above: a `null` means no file can be in both scopes, and a
|
|
291
|
+
* path is a witness a reader can check by eye.
|
|
292
|
+
*/
|
|
293
|
+
export function intersectGlobs(a, b) {
|
|
294
|
+
for (const left of a.alternatives) {
|
|
295
|
+
for (const right of b.alternatives) {
|
|
296
|
+
const witness = intersectSegments(left, right);
|
|
297
|
+
if (witness !== null)
|
|
298
|
+
return witness.length === 0 ? 'x' : witness.join('/');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
const ANY_SEGMENT = [{ kind: 'star' }];
|
|
304
|
+
function intersectSegments(p, q) {
|
|
305
|
+
const memo = new Map();
|
|
306
|
+
const width = q.length + 1;
|
|
307
|
+
const visit = (i, j) => {
|
|
308
|
+
const key = i * width + j;
|
|
309
|
+
const cached = memo.get(key);
|
|
310
|
+
if (cached !== undefined)
|
|
311
|
+
return cached;
|
|
312
|
+
const result = step(i, j);
|
|
313
|
+
memo.set(key, result);
|
|
314
|
+
return result;
|
|
315
|
+
};
|
|
316
|
+
const prepend = (head, tail) => head === null || tail === null ? null : [head, ...tail];
|
|
317
|
+
const step = (i, j) => {
|
|
318
|
+
if (i === p.length && j === q.length)
|
|
319
|
+
return [];
|
|
320
|
+
const x = p[i];
|
|
321
|
+
const y = q[j];
|
|
322
|
+
if (x?.kind === 'globstar') {
|
|
323
|
+
const skip = visit(i + 1, j);
|
|
324
|
+
if (skip !== null)
|
|
325
|
+
return skip;
|
|
326
|
+
if (y === undefined)
|
|
327
|
+
return null;
|
|
328
|
+
if (y.kind === 'globstar')
|
|
329
|
+
return visit(i, j + 1);
|
|
330
|
+
return prepend(intersectTokens(y.tokens, ANY_SEGMENT), visit(i, j + 1));
|
|
331
|
+
}
|
|
332
|
+
if (y?.kind === 'globstar') {
|
|
333
|
+
const skip = visit(i, j + 1);
|
|
334
|
+
if (skip !== null)
|
|
335
|
+
return skip;
|
|
336
|
+
if (x === undefined)
|
|
337
|
+
return null;
|
|
338
|
+
return prepend(intersectTokens(x.tokens, ANY_SEGMENT), visit(i + 1, j));
|
|
339
|
+
}
|
|
340
|
+
if (x === undefined || y === undefined)
|
|
341
|
+
return null;
|
|
342
|
+
const head = intersectTokens(x.tokens, y.tokens);
|
|
343
|
+
return head === null ? null : prepend(head, visit(i + 1, j + 1));
|
|
344
|
+
};
|
|
345
|
+
return visit(0, 0);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* A non-empty string both token sequences match, or `null`.
|
|
349
|
+
*
|
|
350
|
+
* Complete by a shortest-witness argument: in a shortest common string, no
|
|
351
|
+
* character is absorbed by a star on both sides at once (dropping it would
|
|
352
|
+
* leave a shorter one), so every character advances at least one sequence,
|
|
353
|
+
* and the five moves below are all the ways that can happen.
|
|
354
|
+
*/
|
|
355
|
+
export function intersectTokens(x, y) {
|
|
356
|
+
const memo = new Map();
|
|
357
|
+
const width = y.length + 1;
|
|
358
|
+
const visit = (i, j) => {
|
|
359
|
+
const key = i * width + j;
|
|
360
|
+
const cached = memo.get(key);
|
|
361
|
+
if (cached !== undefined)
|
|
362
|
+
return cached;
|
|
363
|
+
const result = step(i, j);
|
|
364
|
+
memo.set(key, result);
|
|
365
|
+
return result;
|
|
366
|
+
};
|
|
367
|
+
const prepend = (head, tail) => head === null || tail === null ? null : head + tail;
|
|
368
|
+
const step = (i, j) => {
|
|
369
|
+
if (i === x.length && j === y.length)
|
|
370
|
+
return '';
|
|
371
|
+
const a = x[i];
|
|
372
|
+
const b = y[j];
|
|
373
|
+
if (a?.kind === 'star') {
|
|
374
|
+
const skip = visit(i + 1, j);
|
|
375
|
+
if (skip !== null)
|
|
376
|
+
return skip;
|
|
377
|
+
if (b === undefined)
|
|
378
|
+
return null;
|
|
379
|
+
if (b.kind === 'star')
|
|
380
|
+
return visit(i, j + 1);
|
|
381
|
+
return prepend(witnessChar(b), visit(i, j + 1));
|
|
382
|
+
}
|
|
383
|
+
if (b?.kind === 'star') {
|
|
384
|
+
const skip = visit(i, j + 1);
|
|
385
|
+
if (skip !== null)
|
|
386
|
+
return skip;
|
|
387
|
+
if (a === undefined)
|
|
388
|
+
return null;
|
|
389
|
+
return prepend(witnessChar(a), visit(i + 1, j));
|
|
390
|
+
}
|
|
391
|
+
if (a === undefined || b === undefined)
|
|
392
|
+
return null;
|
|
393
|
+
return prepend(meet(a, b), visit(i + 1, j + 1));
|
|
394
|
+
};
|
|
395
|
+
const witness = visit(0, 0);
|
|
396
|
+
// Empty only when both sides are nothing but stars, and then any name will do.
|
|
397
|
+
return witness === '' ? 'x' : witness;
|
|
398
|
+
}
|
|
399
|
+
function accepts(token, point) {
|
|
400
|
+
if (point === SLASH)
|
|
401
|
+
return false;
|
|
402
|
+
switch (token.kind) {
|
|
403
|
+
case 'literal':
|
|
404
|
+
return token.char.codePointAt(0) === point;
|
|
405
|
+
case 'any':
|
|
406
|
+
return true;
|
|
407
|
+
case 'class':
|
|
408
|
+
return inClass(token, point);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Every point where the set of characters all the tokens accept could begin.
|
|
413
|
+
*
|
|
414
|
+
* Each token accepts a union of intervals: a literal one point, a positive
|
|
415
|
+
* class its ranges, a negated class and `?` everything outside theirs. Less the
|
|
416
|
+
* slash. The least character of an intersection of such sets is the left end
|
|
417
|
+
* of one of its intervals, so it is one of these points, and checking them
|
|
418
|
+
* all decides the intersection exactly. `0` is also the point after the
|
|
419
|
+
* slash, and readable characters go first, so that a witness is one a person
|
|
420
|
+
* would type.
|
|
421
|
+
*/
|
|
422
|
+
function candidates(tokens) {
|
|
423
|
+
const points = [0x78, 0x61, 0x30, 0x5f, 0x2d, 0x21, 0];
|
|
424
|
+
for (const token of tokens) {
|
|
425
|
+
if (token.kind === 'literal')
|
|
426
|
+
points.push(token.char.codePointAt(0));
|
|
427
|
+
if (token.kind === 'class') {
|
|
428
|
+
for (const [lo, hi] of token.ranges)
|
|
429
|
+
points.push(lo, hi + 1);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return points;
|
|
433
|
+
}
|
|
434
|
+
/** A character every token accepts, or `null` when there is none. */
|
|
435
|
+
function pick(tokens) {
|
|
436
|
+
for (const point of candidates(tokens)) {
|
|
437
|
+
if (point <= 0x10ffff && tokens.every((t) => accepts(t, point)))
|
|
438
|
+
return String.fromCodePoint(point);
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
function witnessChar(token) {
|
|
443
|
+
return pick([token]);
|
|
444
|
+
}
|
|
445
|
+
function meet(a, b) {
|
|
446
|
+
return pick([a, b]);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* The directory a glob is rooted in: its leading literal segments. A pattern
|
|
450
|
+
* that names a file is rooted in that file's directory.
|
|
451
|
+
*/
|
|
452
|
+
export function globBase(glob) {
|
|
453
|
+
const first = glob.alternatives[0];
|
|
454
|
+
const literal = [];
|
|
455
|
+
for (const segment of first) {
|
|
456
|
+
if (segment.kind !== 'pattern' || !segment.literal)
|
|
457
|
+
break;
|
|
458
|
+
literal.push(segment.tokens.map((t) => (t.kind === 'literal' ? t.char : '')).join(''));
|
|
459
|
+
}
|
|
460
|
+
// A literal file path is rooted in its directory; a literal directory in itself.
|
|
461
|
+
if (literal.length === first.length)
|
|
462
|
+
literal.pop();
|
|
463
|
+
return literal.join('/');
|
|
464
|
+
}
|
|
465
|
+
//# sourceMappingURL=glob.js.map
|
package/dist/glob.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Globs: parsing, matching, and deciding whether two globs can name the same file.\n *\n * The dialect is the one people type: `**`, `*`, `?`, `[abc]`, `[!a-z]`,\n * `{a,b}` and `\\` to escape. A pattern with no glob syntax at all names a\n * directory and everything beneath it, so `src/auth` covers\n * `src/auth/login.ts` - unless it names a file, one the tree holds or one with\n * an extension, which matches only itself. A trailing `/` always means a\n * directory. Paths\n * are repository-relative and POSIX, and matching is case-sensitive on every\n * host, because git's paths are and a result should not depend on who ran it.\n * `*` matches a leading dot; a scope that forgot its dotfiles is not a scope\n * that excludes them.\n *\n * Nothing here compiles to a `RegExp`. Matching and intersection are dynamic\n * programmes over the pattern and the subject, so a pattern of `*a*a*a*a*b`\n * against a long name costs the product of their lengths and cannot backtrack\n * into an exponent. The same property is why the sibling tools match their own\n * globs this way.\n */\n\nexport type CharToken =\n | { readonly kind: 'literal'; readonly char: string }\n | { readonly kind: 'any' }\n | { readonly kind: 'star' }\n | { readonly kind: 'class'; readonly negated: boolean; readonly ranges: readonly (readonly [number, number])[] };\n\ntype Single = Exclude<CharToken, { kind: 'star' }>;\n\nexport type Segment =\n | { readonly kind: 'globstar' }\n | { readonly kind: 'pattern'; readonly tokens: readonly CharToken[]; readonly literal: boolean };\n\nexport interface Glob {\n readonly source: string;\n /** One sequence of segments per brace alternative; a path matches when any does. */\n readonly alternatives: readonly (readonly Segment[])[];\n}\n\nexport type GlobParse = { readonly ok: true; readonly glob: Glob } | { readonly ok: false; readonly error: string };\n\n/** More alternatives than this is a pattern nobody meant. */\nexport const MAX_ALTERNATIVES = 256;\n\nconst SLASH = 0x2f;\nconst GLOBSTAR: Segment = { kind: 'globstar' };\n\nexport interface ParseOptions {\n /** Whether a literal path is a file, from the tree when it is known. */\n readonly isFile?: ((path: string) => boolean) | undefined;\n}\n\n/** A name with an extension, `login.ts` or `.eslintrc.json`, and not a dot-directory such as `.github`. */\nfunction hasExtension(name: string): boolean {\n return /.\\.[^.]+$/.test(name);\n}\n\nexport function parseGlob(source: string, options: ParseOptions = {}): GlobParse {\n let pattern = source.trim();\n if (pattern.length === 0) return { ok: false, error: 'the pattern is empty' };\n if (pattern.startsWith('!')) return { ok: false, error: 'negated patterns are not supported; narrow the positive pattern' };\n if (pattern.startsWith('/')) return { ok: false, error: 'a pattern is relative to the repository root and cannot start with \"/\"' };\n if (/(?:^|[^\\\\])[@+!?*]\\(/.test(pattern)) return { ok: false, error: 'extended globs such as \"+(a|b)\" are not supported' };\n // A leading \"./\" needs no stripping: \".\" segments are dropped below.\n if (pattern.endsWith('/')) pattern = `${pattern}**`;\n\n const expanded = expandBraces(pattern);\n if (typeof expanded === 'string') return { ok: false, error: expanded };\n\n const alternatives: Segment[][] = [];\n for (const alternative of expanded) {\n const segments: Segment[] = [];\n for (const raw of alternative.split('/')) {\n if (raw === '' || raw === '.') continue;\n if (raw === '..') return { ok: false, error: 'a pattern cannot leave the repository with \"..\"' };\n if (raw === '**') {\n if (segments[segments.length - 1]?.kind !== 'globstar') segments.push(GLOBSTAR);\n continue;\n }\n const tokens = tokenize(raw);\n if (typeof tokens === 'string') return { ok: false, error: tokens };\n segments.push({ kind: 'pattern', tokens, literal: tokens.every((t) => t.kind === 'literal') });\n }\n if (segments.length === 0) return { ok: false, error: 'the pattern names no path' };\n // A path with no glob syntax names a directory and everything beneath it,\n // unless it names a file: one the tree has, or one with an extension.\n // Treating a file as a directory would let \"src/a.ts\" overlap\n // \"**/session.ts\" through a \"src/a.ts/session.ts\" nobody can create.\n if (segments.every((s) => s.kind === 'pattern' && s.literal)) {\n const path = segments\n .map((s) => (s.kind === 'pattern' ? s.tokens.map((t) => (t.kind === 'literal' ? t.char : '')).join('') : ''))\n .join('/');\n const last = path.slice(path.lastIndexOf('/') + 1);\n if (options.isFile?.(path) !== true && !hasExtension(last)) segments.push(GLOBSTAR);\n }\n alternatives.push(segments);\n }\n return { ok: true, glob: { source, alternatives } };\n}\n\n/** Expands `{a,b}` groups, innermost last, or says why the pattern is malformed. */\nfunction expandBraces(pattern: string): string[] | string {\n let open = -1;\n let depth = 0;\n for (let i = 0; i < pattern.length; i += 1) {\n const ch = pattern.charAt(i);\n if (ch === '\\\\') {\n i += 1;\n continue;\n }\n if (ch === '[') {\n const close = classEnd(pattern, i);\n if (close > 0) i = close;\n continue;\n }\n if (ch === '{') {\n if (depth === 0) open = i;\n depth += 1;\n } else if (ch === '}') {\n if (depth === 0) return 'a \"}\" closes no \"{\"';\n depth -= 1;\n if (depth === 0) {\n const options = splitTopLevel(pattern.slice(open + 1, i));\n const prefix = pattern.slice(0, open);\n const suffix = pattern.slice(i + 1);\n const results: string[] = [];\n for (const option of options) {\n const expanded = expandBraces(`${prefix}${option}${suffix}`);\n if (typeof expanded === 'string') return expanded;\n results.push(...expanded);\n if (results.length > MAX_ALTERNATIVES) return `the braces expand to more than ${MAX_ALTERNATIVES} patterns`;\n }\n return results;\n }\n }\n }\n if (depth > 0) return 'a \"{\" is never closed';\n return [pattern];\n}\n\nfunction splitTopLevel(body: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < body.length; i += 1) {\n const ch = body.charAt(i);\n if (ch === '\\\\') i += 1;\n else if (ch === '{') depth += 1;\n else if (ch === '}') depth -= 1;\n else if (ch === ',' && depth === 0) {\n parts.push(body.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(body.slice(start));\n return parts;\n}\n\n/** The index of the `]` closing a class opened at `open`, or -1. */\nfunction classEnd(pattern: string, open: number): number {\n let i = open + 1;\n if (pattern.charAt(i) === '!' || pattern.charAt(i) === '^') i += 1;\n if (pattern.charAt(i) === ']') i += 1;\n for (; i < pattern.length; i += 1) {\n const ch = pattern.charAt(i);\n if (ch === '/') return -1;\n if (ch === ']') return i;\n }\n return -1;\n}\n\nfunction tokenize(segment: string): CharToken[] | string {\n const tokens: CharToken[] = [];\n const chars = Array.from(segment);\n for (let i = 0; i < chars.length; i += 1) {\n const ch = chars[i] as string;\n if (ch === '\\\\') {\n const next = chars[i + 1];\n // A backslash before a letter, a digit or nothing is a Windows separator, not an escape.\n if (next === undefined || /[A-Za-z0-9]/.test(next)) return '\"\\\\\" escapes glob syntax; separate directories with \"/\"';\n tokens.push({ kind: 'literal', char: next });\n i += 1;\n } else if (ch === '*') {\n if (tokens[tokens.length - 1]?.kind !== 'star') tokens.push({ kind: 'star' });\n } else if (ch === '?') {\n tokens.push({ kind: 'any' });\n } else if (ch === '[') {\n const parsed = readClass(chars, i);\n if (typeof parsed === 'string') return parsed;\n tokens.push(parsed.token);\n i = parsed.end;\n } else {\n tokens.push({ kind: 'literal', char: ch });\n }\n }\n return tokens;\n}\n\nfunction readClass(chars: readonly string[], open: number): { token: CharToken; end: number } | string {\n let i = open + 1;\n let negated = false;\n if (chars[i] === '!' || chars[i] === '^') {\n negated = true;\n i += 1;\n }\n const ranges: [number, number][] = [];\n let first = true;\n for (; i < chars.length; i += 1) {\n const ch = chars[i] as string;\n // Never empty: the first character is always a member, even \"]\".\n if (ch === ']' && !first) return { token: { kind: 'class', negated, ranges }, end: i };\n first = false;\n let lo = ch;\n if (ch === '\\\\' && chars[i + 1] !== undefined) {\n i += 1;\n lo = chars[i] as string;\n }\n let hi = lo;\n if (chars[i + 1] === '-' && chars[i + 2] !== undefined && chars[i + 2] !== ']') {\n hi = chars[i + 2] as string;\n i += 2;\n }\n const from = lo.codePointAt(0) as number;\n const to = hi.codePointAt(0) as number;\n if (to < from) return `the range \"${lo}-${hi}\" runs backwards`;\n ranges.push([from, to]);\n }\n return 'a \"[\" is never closed';\n}\n\nfunction splitPath(path: string): string[] {\n return path.split('/').filter((s) => s !== '' && s !== '.');\n}\n\n/** Membership of a class. A slash is never asked about: paths are matched a segment at a time. */\nfunction inClass(token: Extract<CharToken, { kind: 'class' }>, point: number): boolean {\n const inside = token.ranges.some(([lo, hi]) => point >= lo && point <= hi);\n return token.negated ? !inside : inside;\n}\n\nfunction charMatches(token: Single, char: string): boolean {\n switch (token.kind) {\n case 'literal':\n return token.char === char;\n case 'any':\n return true;\n case 'class':\n return inClass(token, char.codePointAt(0) as number);\n }\n}\n\n/** Whether one segment of a path matches a pattern segment's tokens. */\nfunction tokensMatch(tokens: readonly CharToken[], subject: string): boolean {\n const chars = Array.from(subject);\n // next[j]: tokens[i+1..] match chars[j..]; filled from the end of both.\n let next = new Array<boolean>(chars.length + 1).fill(false);\n next[chars.length] = true;\n for (let i = tokens.length - 1; i >= 0; i -= 1) {\n const token = tokens[i] as CharToken;\n const row = new Array<boolean>(chars.length + 1).fill(false);\n for (let j = chars.length; j >= 0; j -= 1) {\n row[j] =\n token.kind === 'star'\n ? (next[j] as boolean) || (j < chars.length && (row[j + 1] as boolean))\n : j < chars.length && charMatches(token, chars[j] as string) && (next[j + 1] as boolean);\n }\n next = row;\n }\n return next[0] as boolean;\n}\n\nfunction segmentsMatch(segments: readonly Segment[], path: readonly string[]): boolean {\n let next = new Array<boolean>(path.length + 1).fill(false);\n next[path.length] = true;\n for (let i = segments.length - 1; i >= 0; i -= 1) {\n const segment = segments[i] as Segment;\n const row = new Array<boolean>(path.length + 1).fill(false);\n for (let j = path.length; j >= 0; j -= 1) {\n row[j] =\n segment.kind === 'globstar'\n ? (next[j] as boolean) || (j < path.length && (row[j + 1] as boolean))\n : j < path.length && tokensMatch(segment.tokens, path[j] as string) && (next[j + 1] as boolean);\n }\n next = row;\n }\n return next[0] as boolean;\n}\n\nexport function matchGlob(glob: Glob, path: string): boolean {\n const segments = splitPath(path);\n if (segments.length === 0) return false;\n return glob.alternatives.some((alternative) => segmentsMatch(alternative, segments));\n}\n\n/**\n * A path both globs match, or `null` when there is none. The answer is exact\n * for the dialect above: a `null` means no file can be in both scopes, and a\n * path is a witness a reader can check by eye.\n */\nexport function intersectGlobs(a: Glob, b: Glob): string | null {\n for (const left of a.alternatives) {\n for (const right of b.alternatives) {\n const witness = intersectSegments(left, right);\n if (witness !== null) return witness.length === 0 ? 'x' : witness.join('/');\n }\n }\n return null;\n}\n\nconst ANY_SEGMENT: readonly CharToken[] = [{ kind: 'star' }];\n\nfunction intersectSegments(p: readonly Segment[], q: readonly Segment[]): string[] | null {\n const memo = new Map<number, string[] | null>();\n const width = q.length + 1;\n const visit = (i: number, j: number): string[] | null => {\n const key = i * width + j;\n const cached = memo.get(key);\n if (cached !== undefined) return cached;\n const result = step(i, j);\n memo.set(key, result);\n return result;\n };\n const prepend = (head: string | null, tail: string[] | null): string[] | null =>\n head === null || tail === null ? null : [head, ...tail];\n\n const step = (i: number, j: number): string[] | null => {\n if (i === p.length && j === q.length) return [];\n const x = p[i];\n const y = q[j];\n if (x?.kind === 'globstar') {\n const skip = visit(i + 1, j);\n if (skip !== null) return skip;\n if (y === undefined) return null;\n if (y.kind === 'globstar') return visit(i, j + 1);\n return prepend(intersectTokens(y.tokens, ANY_SEGMENT), visit(i, j + 1));\n }\n if (y?.kind === 'globstar') {\n const skip = visit(i, j + 1);\n if (skip !== null) return skip;\n if (x === undefined) return null;\n return prepend(intersectTokens(x.tokens, ANY_SEGMENT), visit(i + 1, j));\n }\n if (x === undefined || y === undefined) return null;\n const head = intersectTokens(x.tokens, y.tokens);\n return head === null ? null : prepend(head, visit(i + 1, j + 1));\n };\n return visit(0, 0);\n}\n\n/**\n * A non-empty string both token sequences match, or `null`.\n *\n * Complete by a shortest-witness argument: in a shortest common string, no\n * character is absorbed by a star on both sides at once (dropping it would\n * leave a shorter one), so every character advances at least one sequence,\n * and the five moves below are all the ways that can happen.\n */\nexport function intersectTokens(x: readonly CharToken[], y: readonly CharToken[]): string | null {\n const memo = new Map<number, string | null>();\n const width = y.length + 1;\n const visit = (i: number, j: number): string | null => {\n const key = i * width + j;\n const cached = memo.get(key);\n if (cached !== undefined) return cached;\n const result = step(i, j);\n memo.set(key, result);\n return result;\n };\n const prepend = (head: string | null, tail: string | null): string | null =>\n head === null || tail === null ? null : head + tail;\n\n const step = (i: number, j: number): string | null => {\n if (i === x.length && j === y.length) return '';\n const a = x[i];\n const b = y[j];\n if (a?.kind === 'star') {\n const skip = visit(i + 1, j);\n if (skip !== null) return skip;\n if (b === undefined) return null;\n if (b.kind === 'star') return visit(i, j + 1);\n return prepend(witnessChar(b), visit(i, j + 1));\n }\n if (b?.kind === 'star') {\n const skip = visit(i, j + 1);\n if (skip !== null) return skip;\n if (a === undefined) return null;\n return prepend(witnessChar(a), visit(i + 1, j));\n }\n if (a === undefined || b === undefined) return null;\n return prepend(meet(a, b), visit(i + 1, j + 1));\n };\n const witness = visit(0, 0);\n // Empty only when both sides are nothing but stars, and then any name will do.\n return witness === '' ? 'x' : witness;\n}\n\nfunction accepts(token: Single, point: number): boolean {\n if (point === SLASH) return false;\n switch (token.kind) {\n case 'literal':\n return token.char.codePointAt(0) === point;\n case 'any':\n return true;\n case 'class':\n return inClass(token, point);\n }\n}\n\n/**\n * Every point where the set of characters all the tokens accept could begin.\n *\n * Each token accepts a union of intervals: a literal one point, a positive\n * class its ranges, a negated class and `?` everything outside theirs. Less the\n * slash. The least character of an intersection of such sets is the left end\n * of one of its intervals, so it is one of these points, and checking them\n * all decides the intersection exactly. `0` is also the point after the\n * slash, and readable characters go first, so that a witness is one a person\n * would type.\n */\nfunction candidates(tokens: readonly Single[]): number[] {\n const points = [0x78, 0x61, 0x30, 0x5f, 0x2d, 0x21, 0];\n for (const token of tokens) {\n if (token.kind === 'literal') points.push(token.char.codePointAt(0) as number);\n if (token.kind === 'class') {\n for (const [lo, hi] of token.ranges) points.push(lo, hi + 1);\n }\n }\n return points;\n}\n\n/** A character every token accepts, or `null` when there is none. */\nfunction pick(tokens: readonly Single[]): string | null {\n for (const point of candidates(tokens)) {\n if (point <= 0x10ffff && tokens.every((t) => accepts(t, point))) return String.fromCodePoint(point);\n }\n return null;\n}\n\nfunction witnessChar(token: Single): string | null {\n return pick([token]);\n}\n\nfunction meet(a: Single, b: Single): string | null {\n return pick([a, b]);\n}\n\n/**\n * The directory a glob is rooted in: its leading literal segments. A pattern\n * that names a file is rooted in that file's directory.\n */\nexport function globBase(glob: Glob): string {\n const first = glob.alternatives[0] as readonly Segment[];\n const literal: string[] = [];\n for (const segment of first) {\n if (segment.kind !== 'pattern' || !segment.literal) break;\n literal.push(segment.tokens.map((t) => (t.kind === 'literal' ? t.char : '')).join(''));\n }\n // A literal file path is rooted in its directory; a literal directory in itself.\n if (literal.length === first.length) literal.pop();\n return literal.join('/');\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The library. Everything the CLI does is reachable from here, and the
|
|
3
|
+
* CLI is built from nothing else.
|
|
4
|
+
*/
|
|
5
|
+
export { applyPlan, ConflictError, TransactionError } from './apply.js';
|
|
6
|
+
export { type ArchiveRequest, type FileOp, openTasks, type Plan, planArchive, planUnarchive, type PullRequest, renderBanner } from './archive.js';
|
|
7
|
+
export { BANNER_CLOSE, BANNER_OPEN, type Brief, BUILT_IN_FIELDS, type FieldProblem, idFromName, parseBrief } from './brief.js';
|
|
8
|
+
export { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';
|
|
9
|
+
export { type Collision, collisionFindings, type CollisionOptions, type CollisionReport, collisions, type SharedDirectory, type WaveMatrix, } from './collisions.js';
|
|
10
|
+
export { BANNER_PLACEHOLDERS, type Config, CONFIG_FILES, CONFIG_SCHEMA, ConfigError, configJsonSchema, DEFAULT_CONFIG, parseConfig, type PluginReference, resolveConfig, type SectionRule, } from './config.js';
|
|
11
|
+
export { buildCorpus, type Corpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, type SourceFile, } from './corpus.js';
|
|
12
|
+
export { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';
|
|
13
|
+
export { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';
|
|
14
|
+
export { type CommitInfo, type FileChange, type Git, NodeGit } from './git.js';
|
|
15
|
+
export { type Glob, globBase, intersectGlobs, matchGlob, parseGlob } from './glob.js';
|
|
16
|
+
export { integrityOf } from './integrity.js';
|
|
17
|
+
export { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise } from './lint.js';
|
|
18
|
+
export { asPlugin, loadPlugins } from './plugins.js';
|
|
19
|
+
export { type Format, FORMATS, githubCommands, JSON_SCHEMA_VERSION, sarif } from './report.js';
|
|
20
|
+
export { COLLISION_RULES, type Rule, type RuleContext, type RuleInfo, type RuleResult, RULES } from './rules.js';
|
|
21
|
+
export { nextId, renderNewBrief } from './scaffold.js';
|
|
22
|
+
export type { Finding, Phase, Severity, SeveritySetting, Status } from './types.js';
|
|
23
|
+
/** Types a plugin author writes against, with inference for the rule list. */
|
|
24
|
+
export declare function definePlugin<T extends import('./lint.js').Plugin>(plugin: T): T;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The library. Everything the CLI does is reachable from here, and the
|
|
3
|
+
* CLI is built from nothing else.
|
|
4
|
+
*/
|
|
5
|
+
export { applyPlan, ConflictError, TransactionError } from './apply.js';
|
|
6
|
+
export { openTasks, planArchive, planUnarchive, renderBanner } from './archive.js';
|
|
7
|
+
export { BANNER_CLOSE, BANNER_OPEN, BUILT_IN_FIELDS, idFromName, parseBrief } from './brief.js';
|
|
8
|
+
export { main, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';
|
|
9
|
+
export { collisionFindings, collisions, } from './collisions.js';
|
|
10
|
+
export { BANNER_PLACEHOLDERS, CONFIG_FILES, CONFIG_SCHEMA, ConfigError, configJsonSchema, DEFAULT_CONFIG, parseConfig, resolveConfig, } from './config.js';
|
|
11
|
+
export { buildCorpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, } from './corpus.js';
|
|
12
|
+
export { BriefEngine, EngineError, today } from './engine.js';
|
|
13
|
+
export { MemoryFileSystem, NodeFileSystem } from './fs.js';
|
|
14
|
+
export { NodeGit } from './git.js';
|
|
15
|
+
export { globBase, intersectGlobs, matchGlob, parseGlob } from './glob.js';
|
|
16
|
+
export { integrityOf } from './integrity.js';
|
|
17
|
+
export { failing, lint, ruleIds, sortFindings, summarise } from './lint.js';
|
|
18
|
+
export { asPlugin, loadPlugins } from './plugins.js';
|
|
19
|
+
export { FORMATS, githubCommands, JSON_SCHEMA_VERSION, sarif } from './report.js';
|
|
20
|
+
export { COLLISION_RULES, RULES } from './rules.js';
|
|
21
|
+
export { nextId, renderNewBrief } from './scaffold.js';
|
|
22
|
+
/** Types a plugin author writes against, with inference for the rule list. */
|
|
23
|
+
export function definePlugin(plugin) {
|
|
24
|
+
return plugin;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAoC,SAAS,EAAa,WAAW,EAAE,aAAa,EAAoB,YAAY,EAAE,MAAM,cAAc,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,WAAW,EAAc,eAAe,EAAqB,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAEL,iBAAiB,EAGjB,UAAU,GAGX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAEnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EAEX,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EAEX,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,iBAAiB,GAElB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,WAAW,EAAE,WAAW,EAAuD,KAAK,EAAE,MAAM,aAAa,CAAC;AACxI,OAAO,EAAmB,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAA8C,OAAO,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAa,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAiC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3G,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAe,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EAAE,eAAe,EAA+D,KAAK,EAAE,MAAM,YAAY,CAAC;AACjH,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGvD,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAuC,MAAS;IAC1E,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * The library. Everything the CLI does is reachable from here, and the\n * CLI is built from nothing else.\n */\n\nexport { applyPlan, ConflictError, TransactionError } from './apply.js';\nexport { type ArchiveRequest, type FileOp, openTasks, type Plan, planArchive, planUnarchive, type PullRequest, renderBanner } from './archive.js';\nexport { BANNER_CLOSE, BANNER_OPEN, type Brief, BUILT_IN_FIELDS, type FieldProblem, idFromName, parseBrief } from './brief.js';\nexport { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';\nexport {\n type Collision,\n collisionFindings,\n type CollisionOptions,\n type CollisionReport,\n collisions,\n type SharedDirectory,\n type WaveMatrix,\n} from './collisions.js';\nexport {\n BANNER_PLACEHOLDERS,\n type Config,\n CONFIG_FILES,\n CONFIG_SCHEMA,\n ConfigError,\n configJsonSchema,\n DEFAULT_CONFIG,\n parseConfig,\n type PluginReference,\n resolveConfig,\n type SectionRule,\n} from './config.js';\nexport {\n buildCorpus,\n type Corpus,\n dependencyCycles,\n findBriefs,\n isReady,\n pendingDependencies,\n resolveDependency,\n type SourceFile,\n} from './corpus.js';\nexport { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';\nexport { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';\nexport { type CommitInfo, type FileChange, type Git, NodeGit } from './git.js';\nexport { type Glob, globBase, intersectGlobs, matchGlob, parseGlob } from './glob.js';\nexport { integrityOf } from './integrity.js';\nexport { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise } from './lint.js';\nexport { asPlugin, loadPlugins } from './plugins.js';\nexport { type Format, FORMATS, githubCommands, JSON_SCHEMA_VERSION, sarif } from './report.js';\nexport { COLLISION_RULES, type Rule, type RuleContext, type RuleInfo, type RuleResult, RULES } from './rules.js';\nexport { nextId, renderNewBrief } from './scaffold.js';\nexport type { Finding, Phase, Severity, SeveritySetting, Status } from './types.js';\n\n/** Types a plugin author writes against, with inference for the rule list. */\nexport function definePlugin<T extends import('./lint.js').Plugin>(plugin: T): T {\n return plugin;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The freeze: a content hash an archived brief carries in its own front matter.
|
|
3
|
+
*
|
|
4
|
+
* Kept in the file rather than in a ledger beside it, because a ledger every
|
|
5
|
+
* archival appends to is one more file that two rounds merging in parallel
|
|
6
|
+
* both edit. The hash covers every line except its own, after CRLF and a
|
|
7
|
+
* byte-order mark are normalised away, so a checkout with `core.autocrlf`
|
|
8
|
+
* reads as unchanged and an edited word does not.
|
|
9
|
+
*/
|
|
10
|
+
export declare const INTEGRITY_FIELD = "integrity";
|
|
11
|
+
export declare function integrityOf(text: string): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The freeze: a content hash an archived brief carries in its own front matter.
|
|
3
|
+
*
|
|
4
|
+
* Kept in the file rather than in a ledger beside it, because a ledger every
|
|
5
|
+
* archival appends to is one more file that two rounds merging in parallel
|
|
6
|
+
* both edit. The hash covers every line except its own, after CRLF and a
|
|
7
|
+
* byte-order mark are normalised away, so a checkout with `core.autocrlf`
|
|
8
|
+
* reads as unchanged and an edited word does not.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { readFrontMatter, removeEntry } from './frontmatter.js';
|
|
12
|
+
import { canonicalText, splitLines } from './text.js';
|
|
13
|
+
export const INTEGRITY_FIELD = 'integrity';
|
|
14
|
+
export function integrityOf(text) {
|
|
15
|
+
const lines = splitLines(canonicalText(text));
|
|
16
|
+
const without = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);
|
|
17
|
+
const digest = createHash('sha256').update(`${without.join('\n')}\n`).digest('hex');
|
|
18
|
+
return `sha256-${digest}`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpF,OAAO,UAAU,MAAM,EAAE,CAAC;AAC5B,CAAC","sourcesContent":["/**\n * The freeze: a content hash an archived brief carries in its own front matter.\n *\n * Kept in the file rather than in a ledger beside it, because a ledger every\n * archival appends to is one more file that two rounds merging in parallel\n * both edit. The hash covers every line except its own, after CRLF and a\n * byte-order mark are normalised away, so a checkout with `core.autocrlf`\n * reads as unchanged and an edited word does not.\n */\n\nimport { createHash } from 'node:crypto';\n\nimport { readFrontMatter, removeEntry } from './frontmatter.js';\nimport { canonicalText, splitLines } from './text.js';\n\nexport const INTEGRITY_FIELD = 'integrity';\n\nexport function integrityOf(text: string): string {\n const lines = splitLines(canonicalText(text));\n const without = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);\n const digest = createHash('sha256').update(`${without.join('\\n')}\\n`).digest('hex');\n return `sha256-${digest}`;\n}\n"]}
|