@wirecat/cli-base 0.0.0-stage → 0.21.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/LICENSE +204 -0
- package/README.md +376 -2
- package/config/biome.base.json +56 -0
- package/config/cspell.json +57 -0
- package/config/lefthook.yml +34 -0
- package/config/rumdl.toml +9 -0
- package/config/tsconfig.base.json +21 -0
- package/config/tsconfig.scripts.json +13 -0
- package/config/tsconfig.test.json +11 -0
- package/config/vale/CliDocs/Filler.yml +9 -0
- package/config/vale/CliDocs/SentenceLength.yml +6 -0
- package/config/vale/Tg/Tone.yml +7 -0
- package/config/wirecat/LICENSE +204 -0
- package/config/wirecat/biome.base.json +56 -0
- package/config/wirecat/check.mjs +41 -0
- package/config/wirecat/cspell.json +80 -0
- package/config/wirecat/lefthook.yml +26 -0
- package/config/wirecat/manifest.json +23 -0
- package/config/wirecat/markdownlint.json +10 -0
- package/config/wirecat/rumdl.toml +9 -0
- package/dist/codegen/index.d.ts +12 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +11 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/manifest.d.ts +47 -0
- package/dist/codegen/manifest.d.ts.map +1 -0
- package/dist/codegen/manifest.js +73 -0
- package/dist/codegen/manifest.js.map +1 -0
- package/dist/codegen/model.d.ts +146 -0
- package/dist/codegen/model.d.ts.map +1 -0
- package/dist/codegen/model.js +7 -0
- package/dist/codegen/model.js.map +1 -0
- package/dist/codegen/names.d.ts +4 -0
- package/dist/codegen/names.d.ts.map +1 -0
- package/dist/codegen/names.js +46 -0
- package/dist/codegen/names.js.map +1 -0
- package/dist/codegen/pipeline.d.ts +24 -0
- package/dist/codegen/pipeline.d.ts.map +1 -0
- package/dist/codegen/pipeline.js +193 -0
- package/dist/codegen/pipeline.js.map +1 -0
- package/dist/codegen/runtime.d.ts +24 -0
- package/dist/codegen/runtime.d.ts.map +1 -0
- package/dist/codegen/runtime.js +38 -0
- package/dist/codegen/runtime.js.map +1 -0
- package/dist/codegen/tree.d.ts +29 -0
- package/dist/codegen/tree.d.ts.map +1 -0
- package/dist/codegen/tree.js +87 -0
- package/dist/codegen/tree.js.map +1 -0
- package/dist/codegen/typescript.d.ts +6 -0
- package/dist/codegen/typescript.d.ts.map +1 -0
- package/dist/codegen/typescript.js +78 -0
- package/dist/codegen/typescript.js.map +1 -0
- package/dist/codegen/valibot.d.ts +10 -0
- package/dist/codegen/valibot.d.ts.map +1 -0
- package/dist/codegen/valibot.js +132 -0
- package/dist/codegen/valibot.js.map +1 -0
- package/dist/commands/index.d.ts +76 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +62 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/page.d.ts +80 -0
- package/dist/commands/page.d.ts.map +1 -0
- package/dist/commands/page.js +100 -0
- package/dist/commands/page.js.map +1 -0
- package/dist/completion/index.d.ts +37 -0
- package/dist/completion/index.d.ts.map +1 -0
- package/dist/completion/index.js +87 -0
- package/dist/completion/index.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +48 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +120 -0
- package/dist/credentials.js.map +1 -0
- package/dist/deadline.d.ts +14 -0
- package/dist/deadline.d.ts.map +1 -0
- package/dist/deadline.js +88 -0
- package/dist/deadline.js.map +1 -0
- package/dist/dev/bin.d.ts +3 -0
- package/dist/dev/bin.d.ts.map +1 -0
- package/dist/dev/bin.js +8 -0
- package/dist/dev/bin.js.map +1 -0
- package/dist/dev/main.d.ts +7 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/dev/main.js +149 -0
- package/dist/dev/main.js.map +1 -0
- package/dist/dev/next-version.d.ts +11 -0
- package/dist/dev/next-version.d.ts.map +1 -0
- package/dist/dev/next-version.js +33 -0
- package/dist/dev/next-version.js.map +1 -0
- package/dist/dev/slow-tests.d.ts +14 -0
- package/dist/dev/slow-tests.d.ts.map +1 -0
- package/dist/dev/slow-tests.js +17 -0
- package/dist/dev/slow-tests.js.map +1 -0
- package/dist/dev/test-matrix.d.ts +35 -0
- package/dist/dev/test-matrix.d.ts.map +1 -0
- package/dist/dev/test-matrix.js +93 -0
- package/dist/dev/test-matrix.js.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/executable.d.ts +3 -0
- package/dist/executable.d.ts.map +1 -0
- package/dist/executable.js +41 -0
- package/dist/executable.js.map +1 -0
- package/dist/exit-codes.d.ts +11 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +25 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/http/index.d.ts +20 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +46 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/keyring.d.ts +27 -0
- package/dist/keyring.d.ts.map +1 -0
- package/dist/keyring.js +45 -0
- package/dist/keyring.js.map +1 -0
- package/dist/legacy-paths.d.ts +33 -0
- package/dist/legacy-paths.d.ts.map +1 -0
- package/dist/legacy-paths.js +128 -0
- package/dist/legacy-paths.js.map +1 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/logging.d.ts +31 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +58 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp/index.d.ts +32 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +113 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/output-buffer.d.ts +16 -0
- package/dist/output-buffer.d.ts.map +1 -0
- package/dist/output-buffer.js +69 -0
- package/dist/output-buffer.js.map +1 -0
- package/dist/paths.d.ts +35 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +45 -0
- package/dist/paths.js.map +1 -0
- package/dist/pretty.d.ts +12 -0
- package/dist/pretty.d.ts.map +1 -0
- package/dist/pretty.js +83 -0
- package/dist/pretty.js.map +1 -0
- package/dist/release/index.d.ts +38 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +91 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/markdown.d.ts +36 -0
- package/dist/release/markdown.d.ts.map +1 -0
- package/dist/release/markdown.js +153 -0
- package/dist/release/markdown.js.map +1 -0
- package/dist/release/structure.d.ts +16 -0
- package/dist/release/structure.d.ts.map +1 -0
- package/dist/release/structure.js +74 -0
- package/dist/release/structure.js.map +1 -0
- package/dist/renderer.d.ts +34 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +55 -0
- package/dist/renderer.js.map +1 -0
- package/dist/result-fields.d.ts +4 -0
- package/dist/result-fields.d.ts.map +1 -0
- package/dist/result-fields.js +56 -0
- package/dist/result-fields.js.map +1 -0
- package/dist/retry.d.ts +27 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +26 -0
- package/dist/retry.js.map +1 -0
- package/dist/sanitize.d.ts +25 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +52 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/skill/index.d.ts +78 -0
- package/dist/skill/index.d.ts.map +1 -0
- package/dist/skill/index.js +190 -0
- package/dist/skill/index.js.map +1 -0
- package/dist/streams.d.ts +24 -0
- package/dist/streams.d.ts.map +1 -0
- package/dist/streams.js +26 -0
- package/dist/streams.js.map +1 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +29 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/time.d.ts +21 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +19 -0
- package/dist/time.js.map +1 -0
- package/dist/update/index.d.ts +76 -0
- package/dist/update/index.d.ts.map +1 -0
- package/dist/update/index.js +160 -0
- package/dist/update/index.js.map +1 -0
- package/package.json +126 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The prefixes of the owner's journal ids; a project adds its own (`CLI`, `MAX`, …). */
|
|
2
|
+
export declare const JOURNAL_IDS: readonly ["NEED", "FIND", "BUG", "SEC", "PERF", "UX", "IDEA", "RISK", "DEBT", "ASK", "TASK"];
|
|
3
|
+
export declare const idPattern: (prefixes: readonly string[]) => RegExp;
|
|
4
|
+
export type ChangelogRules = {
|
|
5
|
+
/** The only `###` headings a section may have. */
|
|
6
|
+
headings: readonly string[];
|
|
7
|
+
/** The top section's title until it is dated, `Unreleased` or `Не выпущено`. */
|
|
8
|
+
unreleased: string;
|
|
9
|
+
ids: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* `release`: the top section must be `version`, and nothing may be left unreleased — a release
|
|
13
|
+
* workflow builds its notes by matching `## <version> `, so a heading slightly off publishes empty
|
|
14
|
+
* notes without an error.
|
|
15
|
+
*/
|
|
16
|
+
export declare const changelogProblems: (text: string, { version, release, headings, unreleased, ids }: ChangelogRules & {
|
|
17
|
+
version?: string;
|
|
18
|
+
release: boolean;
|
|
19
|
+
}) => string[];
|
|
20
|
+
/** GitHub's heading anchor: lower case, only letters, digits, `-`, `_` and spaces kept, spaces to `-`. */
|
|
21
|
+
export declare const slug: (heading: string) => string;
|
|
22
|
+
export declare const withoutCode: (text: string) => string;
|
|
23
|
+
/** Every `.md` under `directory`, not descending into a directory named in `skip`. */
|
|
24
|
+
export declare const markdownFiles: (directory: string, skip?: ReadonlySet<string>) => string[];
|
|
25
|
+
export type DocsRules = {
|
|
26
|
+
/** Absolute paths; a missing one is passed over. */
|
|
27
|
+
files: readonly string[];
|
|
28
|
+
ids: readonly string[];
|
|
29
|
+
/** A page a user reads, by its path relative to `root`: no correction mark, struck-out text or id. */
|
|
30
|
+
userPage?: (name: string) => boolean;
|
|
31
|
+
correction?: RegExp;
|
|
32
|
+
/** A link not to follow — a private or sibling checkout that CI does not have. */
|
|
33
|
+
skipLink?: (target: string, destination: string) => boolean;
|
|
34
|
+
};
|
|
35
|
+
export declare const docsProblems: (root: string, { files, ids, userPage, correction, skipLink }: DocsRules) => string[];
|
|
36
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/release/markdown.ts"],"names":[],"mappings":"AAGA,yFAAyF;AACzF,eAAO,MAAM,WAAW,YAAI,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAU,CAAA;AAEvH,eAAO,MAAM,SAAS,aAAc,SAAS,MAAM,EAAE,WAA4D,CAAA;AAIjH,MAAM,MAAM,cAAc,GAAG;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CACvB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,mDACqC,cAAc,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,aAyCzG,CAAA;AAED,0GAA0G;AAC1G,eAAO,MAAM,IAAI,YAAa,MAAM,WAMb,CAAA;AAEvB,eAAO,MAAM,WAAW,SAAU,MAAM,WAGqB,CAAA;AAsB7D,sFAAsF;AACtF,eAAO,MAAM,aAAa,cAAe,MAAM,SAAQ,WAAW,CAAC,MAAM,CAAC,KAAe,MAAM,EAWvF,CAAA;AAER,MAAM,MAAM,SAAS,GAAG;IACtB,oDAAoD;IACpD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;IACtB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAA;CAC5D,CAAA;AAED,eAAO,MAAM,YAAY,SACjB,MAAM,kDACsF,SAAS,aA8C5G,CAAA"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
/** The prefixes of the owner's journal ids; a project adds its own (`CLI`, `MAX`, …). */
|
|
4
|
+
export const JOURNAL_IDS = ["NEED", "FIND", "BUG", "SEC", "PERF", "UX", "IDEA", "RISK", "DEBT", "ASK", "TASK"];
|
|
5
|
+
export const idPattern = (prefixes) => new RegExp(`\\b(?:${prefixes.join("|")})-\\d+\\b`, "g");
|
|
6
|
+
const VERSION_HEADING = /^## (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?) — (\d{2})\.(\d{2})\.(\d{4})$/;
|
|
7
|
+
/**
|
|
8
|
+
* `release`: the top section must be `version`, and nothing may be left unreleased — a release
|
|
9
|
+
* workflow builds its notes by matching `## <version> `, so a heading slightly off publishes empty
|
|
10
|
+
* notes without an error.
|
|
11
|
+
*/
|
|
12
|
+
export const changelogProblems = (text, { version, release, headings, unreleased, ids }) => {
|
|
13
|
+
const problems = [];
|
|
14
|
+
const id = idPattern(ids);
|
|
15
|
+
let section;
|
|
16
|
+
let subheadings = new Set();
|
|
17
|
+
let first = true;
|
|
18
|
+
text.split("\n").forEach((line, index) => {
|
|
19
|
+
const where = `CHANGELOG.md:${index + 1}`;
|
|
20
|
+
if (line.startsWith("## ")) {
|
|
21
|
+
section = line.slice(3);
|
|
22
|
+
subheadings = new Set();
|
|
23
|
+
if (section === unreleased) {
|
|
24
|
+
if (release)
|
|
25
|
+
problems.push(`${where}: "${unreleased}" is left — date it as ${version} before releasing`);
|
|
26
|
+
else if (!first)
|
|
27
|
+
problems.push(`${where}: "${unreleased}" must be the top section`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const found = VERSION_HEADING.exec(line);
|
|
31
|
+
if (!found)
|
|
32
|
+
problems.push(`${where}: "${line}" is not "## <version> — DD.MM.YYYY"`);
|
|
33
|
+
else {
|
|
34
|
+
const [, heading, day, month] = found;
|
|
35
|
+
if (Number(day) < 1 || Number(day) > 31 || Number(month) < 1 || Number(month) > 12)
|
|
36
|
+
problems.push(`${where}: "${line}" has no such date`);
|
|
37
|
+
if (release && first && heading !== version)
|
|
38
|
+
problems.push(`${where}: the top section is ${heading}, package.json says ${version}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
first = false;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (line.startsWith("### ")) {
|
|
45
|
+
const heading = line.slice(4);
|
|
46
|
+
if (!headings.includes(heading))
|
|
47
|
+
problems.push(`${where}: "${heading}" is not one of: ${headings.join(", ")}`);
|
|
48
|
+
if (subheadings.has(heading))
|
|
49
|
+
problems.push(`${where}: "${heading}" twice in "${section}"`);
|
|
50
|
+
subheadings.add(heading);
|
|
51
|
+
}
|
|
52
|
+
for (const found of line.match(id) ?? [])
|
|
53
|
+
problems.push(`${where}: internal id ${found} — say what changed instead`);
|
|
54
|
+
});
|
|
55
|
+
if (release && first)
|
|
56
|
+
problems.push(`CHANGELOG.md: no section for ${version}`);
|
|
57
|
+
return problems;
|
|
58
|
+
};
|
|
59
|
+
/** GitHub's heading anchor: lower case, only letters, digits, `-`, `_` and spaces kept, spaces to `-`. */
|
|
60
|
+
export const slug = (heading) => heading
|
|
61
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
62
|
+
.replace(/<[^>]+>/g, "")
|
|
63
|
+
.toLowerCase()
|
|
64
|
+
.replace(/[^\p{L}\p{M}\p{N}\-_ ]/gu, "")
|
|
65
|
+
.replace(/ /g, "-");
|
|
66
|
+
export const withoutCode = (text) => text
|
|
67
|
+
.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, (block) => block.replace(/[^\n]/g, " "))
|
|
68
|
+
.replace(/`[^`\n]*`/g, (span) => " ".repeat(span.length));
|
|
69
|
+
// Headings are read from the raw text, so code inside a heading still counts toward its anchor.
|
|
70
|
+
const anchorsOf = (text) => {
|
|
71
|
+
const raw = text.split("\n");
|
|
72
|
+
const anchors = new Set();
|
|
73
|
+
const counts = new Map();
|
|
74
|
+
withoutCode(text)
|
|
75
|
+
.split("\n")
|
|
76
|
+
.forEach((masked, index) => {
|
|
77
|
+
if (!/^#{1,6} /.test(masked))
|
|
78
|
+
return;
|
|
79
|
+
const heading = /^#{1,6} (.+?)\s*#*\s*$/.exec(raw[index] ?? "")?.[1];
|
|
80
|
+
if (heading === undefined)
|
|
81
|
+
return;
|
|
82
|
+
const base = slug(heading);
|
|
83
|
+
const seen = counts.get(base) ?? 0;
|
|
84
|
+
counts.set(base, seen + 1);
|
|
85
|
+
anchors.add(seen === 0 ? base : `${base}-${seen}`);
|
|
86
|
+
});
|
|
87
|
+
for (const [, name = ""] of text.matchAll(/<a\s+(?:name|id)="([^"]+)"/g))
|
|
88
|
+
anchors.add(name);
|
|
89
|
+
return anchors;
|
|
90
|
+
};
|
|
91
|
+
/** Every `.md` under `directory`, not descending into a directory named in `skip`. */
|
|
92
|
+
export const markdownFiles = (directory, skip = new Set()) => existsSync(directory)
|
|
93
|
+
? readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
94
|
+
if (skip.has(entry.name))
|
|
95
|
+
return [];
|
|
96
|
+
const path = join(directory, entry.name);
|
|
97
|
+
// stat, not the entry's own type: Claude Code's sandbox mounts character devices over names
|
|
98
|
+
// like .claude/loop.md, and the directory listing still reports what lies underneath.
|
|
99
|
+
const stats = statSync(path, { throwIfNoEntry: false });
|
|
100
|
+
if (entry.isDirectory() && stats?.isDirectory())
|
|
101
|
+
return markdownFiles(path, skip);
|
|
102
|
+
return stats?.isFile() && entry.name.endsWith(".md") ? [path] : [];
|
|
103
|
+
})
|
|
104
|
+
: [];
|
|
105
|
+
export const docsProblems = (root, { files, ids, userPage = () => false, correction = /correction \d{4}/i, skipLink = () => false }) => {
|
|
106
|
+
const id = idPattern(ids);
|
|
107
|
+
const cache = new Map();
|
|
108
|
+
const anchors = (path) => {
|
|
109
|
+
let found = cache.get(path);
|
|
110
|
+
if (!found) {
|
|
111
|
+
found = anchorsOf(readFileSync(path, "utf8"));
|
|
112
|
+
cache.set(path, found);
|
|
113
|
+
}
|
|
114
|
+
return found;
|
|
115
|
+
};
|
|
116
|
+
const problems = [];
|
|
117
|
+
for (const path of files.filter((file) => existsSync(file))) {
|
|
118
|
+
const name = relative(root, path);
|
|
119
|
+
const user = userPage(name);
|
|
120
|
+
withoutCode(readFileSync(path, "utf8"))
|
|
121
|
+
.split("\n")
|
|
122
|
+
.forEach((line, index) => {
|
|
123
|
+
const where = `${name}:${index + 1}`;
|
|
124
|
+
for (const [, target] of line.matchAll(/!?\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g)) {
|
|
125
|
+
if (!target || /^[a-z][a-z0-9+.-]*:/i.test(target))
|
|
126
|
+
continue;
|
|
127
|
+
const [file = "", anchor] = decodeURIComponent(target).split("#");
|
|
128
|
+
const destination = file === "" ? path : resolve(dirname(path), file);
|
|
129
|
+
if (skipLink(file, destination))
|
|
130
|
+
continue;
|
|
131
|
+
if (!existsSync(destination)) {
|
|
132
|
+
problems.push(`${where}: link to ${file} — no such file`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (anchor &&
|
|
136
|
+
destination.endsWith(".md") &&
|
|
137
|
+
statSync(destination).isFile() &&
|
|
138
|
+
!anchors(destination).has(anchor))
|
|
139
|
+
problems.push(`${where}: link to ${file}#${anchor} — no such heading`);
|
|
140
|
+
}
|
|
141
|
+
if (!user)
|
|
142
|
+
return;
|
|
143
|
+
if (correction.test(line))
|
|
144
|
+
problems.push(`${where}: a correction mark on a user page`);
|
|
145
|
+
if (line.includes("~~"))
|
|
146
|
+
problems.push(`${where}: struck-out text on a user page`);
|
|
147
|
+
for (const found of line.match(id) ?? [])
|
|
148
|
+
problems.push(`${where}: internal id ${found} on a user page`);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return problems;
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/release/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The pages every tool has — the shared page set in cli-docs' STRUCTURE.md. */
|
|
2
|
+
export declare const REQUIRED_PAGES: readonly ["index", "installation", "usage", "commands", "configuration", "troubleshooting", "security"];
|
|
3
|
+
export type StructureRules = {
|
|
4
|
+
/** The docs folder, relative to `root`. */
|
|
5
|
+
docs?: string;
|
|
6
|
+
required?: readonly string[];
|
|
7
|
+
/** Pages in `meta.json` that are not files in the folder: `changelog` comes from the root. */
|
|
8
|
+
external?: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The docs folder against its `meta.json`: every listed page exists, every page is listed once,
|
|
12
|
+
* the required ones are there, and each page has exactly one `# heading`, its title. `README.md`
|
|
13
|
+
* is the contributors' index and stays out of the sidebar.
|
|
14
|
+
*/
|
|
15
|
+
export declare const structureProblems: (root: string, { docs, required, external }?: StructureRules) => string[];
|
|
16
|
+
//# sourceMappingURL=structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/release/structure.ts"],"names":[],"mappings":"AAIA,gFAAgF;AAChF,eAAO,MAAM,cAAc,YACzB,OAAO,EACP,cAAc,EACd,OAAO,EACP,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,UAAU,CACF,CAAA;AAKV,MAAM,MAAM,cAAc,GAAG;IAC3B,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,iCAC4D,cAAc,aA+CvF,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { withoutCode } from "./markdown.js";
|
|
4
|
+
/** The pages every tool has — the shared page set in cli-docs' STRUCTURE.md. */
|
|
5
|
+
export const REQUIRED_PAGES = [
|
|
6
|
+
"index",
|
|
7
|
+
"installation",
|
|
8
|
+
"usage",
|
|
9
|
+
"commands",
|
|
10
|
+
"configuration",
|
|
11
|
+
"troubleshooting",
|
|
12
|
+
"security",
|
|
13
|
+
];
|
|
14
|
+
// Fumadocs' own keys (fumadocs.dev/docs/page-conventions); the portal reads meta.json as it is.
|
|
15
|
+
const META_KEYS = new Set(["title", "icon", "pages", "defaultOpen", "collapsible", "pagesIndex", "root"]);
|
|
16
|
+
/**
|
|
17
|
+
* The docs folder against its `meta.json`: every listed page exists, every page is listed once,
|
|
18
|
+
* the required ones are there, and each page has exactly one `# heading`, its title. `README.md`
|
|
19
|
+
* is the contributors' index and stays out of the sidebar.
|
|
20
|
+
*/
|
|
21
|
+
export const structureProblems = (root, { docs = "docs", required = REQUIRED_PAGES, external = ["changelog"] } = {}) => {
|
|
22
|
+
const folder = join(root, docs);
|
|
23
|
+
const metaPath = join(folder, "meta.json");
|
|
24
|
+
if (!existsSync(metaPath))
|
|
25
|
+
return [`${docs}/meta.json: missing — it lists the pages in sidebar order`];
|
|
26
|
+
let meta;
|
|
27
|
+
try {
|
|
28
|
+
meta = JSON.parse(readFileSync(metaPath, "utf8"));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return [`${docs}/meta.json: not JSON — ${error.message}`];
|
|
32
|
+
}
|
|
33
|
+
const problems = [];
|
|
34
|
+
for (const key of Object.keys(meta))
|
|
35
|
+
if (!META_KEYS.has(key))
|
|
36
|
+
problems.push(`${docs}/meta.json: "${key}" is not a Fumadocs key`);
|
|
37
|
+
const listed = Array.isArray(meta.pages) ? meta.pages.filter((page) => typeof page === "string") : [];
|
|
38
|
+
if (!Array.isArray(meta.pages))
|
|
39
|
+
problems.push(`${docs}/meta.json: "pages" must be a list`);
|
|
40
|
+
const pages = listed.filter((page) => !/^---.*---$/.test(page) && !/^\[.*\]\(.*\)$/.test(page));
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
for (const page of pages) {
|
|
43
|
+
if (seen.has(page))
|
|
44
|
+
problems.push(`${docs}/meta.json: "${page}" is listed twice`);
|
|
45
|
+
seen.add(page);
|
|
46
|
+
if (external.includes(page))
|
|
47
|
+
continue;
|
|
48
|
+
if (!existsSync(join(folder, `${page}.md`)))
|
|
49
|
+
problems.push(`${docs}/meta.json: "${page}" — no ${docs}/${page}.md`);
|
|
50
|
+
}
|
|
51
|
+
for (const page of required)
|
|
52
|
+
if (!seen.has(page))
|
|
53
|
+
problems.push(`${docs}/meta.json: "${page}" is required and not listed`);
|
|
54
|
+
const files = readdirSync(folder).filter((name) => name.endsWith(".md") && name !== "README.md");
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
const page = file.slice(0, -3);
|
|
57
|
+
if (!seen.has(page))
|
|
58
|
+
problems.push(`${docs}/${file}: not in meta.json — listed nowhere, so the portal never shows it`);
|
|
59
|
+
const text = readFileSync(join(folder, file), "utf8");
|
|
60
|
+
const titles = withoutCode(text)
|
|
61
|
+
.split("\n")
|
|
62
|
+
.filter((line) => /^# /.test(line)).length;
|
|
63
|
+
if (titles !== 1)
|
|
64
|
+
problems.push(`${docs}/${file}: ${titles} "# " headings — a page has exactly one, its title`);
|
|
65
|
+
const first = text
|
|
66
|
+
.replace(/^(\s*<!--[\s\S]*?-->)*/, "")
|
|
67
|
+
.split("\n")
|
|
68
|
+
.find((line) => line.trim() !== "");
|
|
69
|
+
if (titles === 1 && !first?.startsWith("# "))
|
|
70
|
+
problems.push(`${docs}/${file}: the "# " heading must come first`);
|
|
71
|
+
}
|
|
72
|
+
return problems;
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../src/release/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Streams } from "./streams.js";
|
|
2
|
+
/**
|
|
3
|
+
* What a renderer can be asked for. `auto` is not here on purpose: choosing between a person and a
|
|
4
|
+
* pipe is the caller's job and happens once, so a renderer can never be handed a mode it has to
|
|
5
|
+
* resolve.
|
|
6
|
+
*/
|
|
7
|
+
export declare const RENDER_FORMATS: readonly ["pretty", "json", "jsonl"];
|
|
8
|
+
export type RenderFormat = (typeof RENDER_FORMATS)[number];
|
|
9
|
+
export interface Renderer {
|
|
10
|
+
/** The one thing a caller asked for. In a machine mode this is the whole of stdout. */
|
|
11
|
+
result(value: unknown): void;
|
|
12
|
+
/** Each item on its own line. Only meaningful in `jsonl`; elsewhere it renders the whole list. */
|
|
13
|
+
stream(items: Iterable<unknown>): void;
|
|
14
|
+
note(message: string): void;
|
|
15
|
+
success(message: string): void;
|
|
16
|
+
warn(message: string): void;
|
|
17
|
+
failure(message: string): void;
|
|
18
|
+
}
|
|
19
|
+
export interface RendererOptions {
|
|
20
|
+
format: RenderFormat;
|
|
21
|
+
color: boolean;
|
|
22
|
+
streams?: Streams;
|
|
23
|
+
/** Drops `note`, `success` and `warn`. A failure is still said, and the result is untouched. */
|
|
24
|
+
quiet?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* **Every diagnostic goes to stderr, in every mode — pretty included.**
|
|
28
|
+
*
|
|
29
|
+
* The brief only requires that of the machine modes, but making it unconditional means §44 holds
|
|
30
|
+
* by construction instead of by a mode check somebody can forget. It also means piping human
|
|
31
|
+
* output still gives you the content and not the commentary.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createRenderer: ({ format, color, streams, quiet, }: RendererOptions) => Renderer;
|
|
34
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3D;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAAI,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAU,CAAA;AAClE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,MAAM,WAAW,QAAQ;IACvB,uFAAuF;IACvF,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,kGAAkG;IAClG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;IACtC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gGAAgG;IAChG,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,uCAKxB,eAAe,KAAG,QAyCpB,CAAA"}
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { renderPretty } from "./pretty.js";
|
|
2
|
+
import { visibleControls } from "./sanitize.js";
|
|
3
|
+
import { processStreams } from "./streams.js";
|
|
4
|
+
/**
|
|
5
|
+
* What a renderer can be asked for. `auto` is not here on purpose: choosing between a person and a
|
|
6
|
+
* pipe is the caller's job and happens once, so a renderer can never be handed a mode it has to
|
|
7
|
+
* resolve.
|
|
8
|
+
*/
|
|
9
|
+
export const RENDER_FORMATS = ["pretty", "json", "jsonl"];
|
|
10
|
+
/**
|
|
11
|
+
* **Every diagnostic goes to stderr, in every mode — pretty included.**
|
|
12
|
+
*
|
|
13
|
+
* The brief only requires that of the machine modes, but making it unconditional means §44 holds
|
|
14
|
+
* by construction instead of by a mode check somebody can forget. It also means piping human
|
|
15
|
+
* output still gives you the content and not the commentary.
|
|
16
|
+
*/
|
|
17
|
+
export const createRenderer = ({ format, color, streams = processStreams, quiet = false, }) => {
|
|
18
|
+
// Sanitised in every mode, not just `pretty`: a diagnostic goes to stderr, and stderr is read
|
|
19
|
+
// by a person whatever stdout was asked to be. The message is often the other side's own words.
|
|
20
|
+
const mark = (symbol, message) => format === "pretty" ? `${symbol} ${visibleControls(message)}` : visibleControls(message);
|
|
21
|
+
const say = (symbol) => (message) => {
|
|
22
|
+
if (!quiet)
|
|
23
|
+
streams.diagnostic(mark(symbol, message));
|
|
24
|
+
};
|
|
25
|
+
const diagnostics = {
|
|
26
|
+
note: say("·"),
|
|
27
|
+
success: say("✓"),
|
|
28
|
+
warn: say("!"),
|
|
29
|
+
failure: (message) => streams.diagnostic(mark("✗", message)),
|
|
30
|
+
};
|
|
31
|
+
if (format === "jsonl") {
|
|
32
|
+
return {
|
|
33
|
+
...diagnostics,
|
|
34
|
+
result: (value) => streams.data(JSON.stringify(value)),
|
|
35
|
+
stream: (items) => {
|
|
36
|
+
for (const item of items)
|
|
37
|
+
streams.data(JSON.stringify(item));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (format === "pretty") {
|
|
42
|
+
return {
|
|
43
|
+
...diagnostics,
|
|
44
|
+
result: (value) => streams.data(renderPretty(value, { color })),
|
|
45
|
+
stream: (items) => streams.data(renderPretty([...items], { color })),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// json — exactly one deterministic value, and nothing else, ever.
|
|
49
|
+
return {
|
|
50
|
+
...diagnostics,
|
|
51
|
+
result: (value) => streams.data(JSON.stringify(value)),
|
|
52
|
+
stream: (items) => streams.data(JSON.stringify([...items])),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAgB,MAAM,cAAc,CAAA;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAU,CAAA;AAsBlE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,MAAM,EACN,KAAK,EACL,OAAO,GAAG,cAAc,EACxB,KAAK,GAAG,KAAK,GACG,EAAY,EAAE;IAC9B,8FAA8F;IAC9F,gGAAgG;IAChG,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,EAAE,CAC/C,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IAE1F,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC,OAAe,EAAE,EAAE;QAClD,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACvD,CAAC,CAAA;IAED,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC;QACd,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC;QACjB,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC;QACd,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;KACrE,CAAA;IAED,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO;YACL,GAAG,WAAW;YACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChB,KAAK,MAAM,IAAI,IAAI,KAAK;oBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YAC9D,CAAC;SACF,CAAA;IACH,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO;YACL,GAAG,WAAW;YACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/D,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;SACrE,CAAA;IACH,CAAC;IAED,kEAAkE;IAClE,OAAO;QACL,GAAG,WAAW;QACd,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;KAC5D,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const fieldsOf: (value: string) => string[];
|
|
2
|
+
/** Projects item data while retaining the list envelope and operation identifiers. */
|
|
3
|
+
export declare const projectFields: (value: unknown, fields: readonly string[]) => unknown;
|
|
4
|
+
//# sourceMappingURL=result-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-fields.d.ts","sourceRoot":"","sources":["../src/result-fields.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,MAAM,EAI9C,CAAA;AA6BD,sFAAsF;AACtF,eAAO,MAAM,aAAa,UAAW,OAAO,UAAU,SAAS,MAAM,EAAE,KAAG,OAMzE,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CliError } from "./errors.js";
|
|
2
|
+
const forbidden = new Set(["__proto__", "prototype", "constructor"]);
|
|
3
|
+
const validate = (paths) => {
|
|
4
|
+
if (paths.length === 0 ||
|
|
5
|
+
paths.length > 128 ||
|
|
6
|
+
paths.some((path) => path.length > 256 ||
|
|
7
|
+
!/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)*$/.test(path) ||
|
|
8
|
+
path.split(".").some((part) => forbidden.has(part))))
|
|
9
|
+
throw new CliError("validation_error", "--fields takes comma-separated field paths, for example id,locator,text");
|
|
10
|
+
};
|
|
11
|
+
export const fieldsOf = (value) => {
|
|
12
|
+
const paths = value.split(",").map((one) => one.trim());
|
|
13
|
+
validate(paths);
|
|
14
|
+
return [...new Set(paths)];
|
|
15
|
+
};
|
|
16
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17
|
+
const select = (value, paths, item = false) => {
|
|
18
|
+
if (!object(value))
|
|
19
|
+
return value;
|
|
20
|
+
const fields = item ? paths.map((path) => (path.startsWith("items.") ? path.slice(6) : path)) : paths;
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const path of fields.filter((one) => !fields.some((parent) => parent !== one && one.startsWith(`${parent}.`)))) {
|
|
23
|
+
const parts = path.split(".");
|
|
24
|
+
let found = value;
|
|
25
|
+
for (const part of parts) {
|
|
26
|
+
if (!object(found) || !Object.hasOwn(found, part)) {
|
|
27
|
+
found = undefined;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
found = found[part];
|
|
31
|
+
}
|
|
32
|
+
if (found === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
let target = result;
|
|
35
|
+
for (const part of parts.slice(0, -1)) {
|
|
36
|
+
if (!object(target[part]))
|
|
37
|
+
target[part] = {};
|
|
38
|
+
target = target[part];
|
|
39
|
+
}
|
|
40
|
+
target[parts.at(-1)] = found;
|
|
41
|
+
}
|
|
42
|
+
for (const name of ["operationId", "sendId"])
|
|
43
|
+
if (Object.hasOwn(value, name))
|
|
44
|
+
result[name] = value[name];
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
/** Projects item data while retaining the list envelope and operation identifiers. */
|
|
48
|
+
export const projectFields = (value, fields) => {
|
|
49
|
+
validate(fields);
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return value.map((item) => select(item, fields, true));
|
|
52
|
+
if (object(value) && Array.isArray(value.items))
|
|
53
|
+
return { ...value, items: value.items.map((item) => select(item, fields, true)) };
|
|
54
|
+
return select(value, fields, object(value) && !Object.hasOwn(value, "items"));
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=result-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-fields.js","sourceRoot":"","sources":["../src/result-fields.ts"],"names":[],"mappings":"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"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ErrorCode } from "./errors.js";
|
|
2
|
+
export interface RetryConfig {
|
|
3
|
+
/** Attempts after the first. One means two attempts in total. */
|
|
4
|
+
retries: number;
|
|
5
|
+
baseDelayMs: number;
|
|
6
|
+
maxDelayMs: number;
|
|
7
|
+
/** Beyond this we stop waiting and hand back a structured error instead of blocking. */
|
|
8
|
+
maxRetryAfterMs: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_RETRY: RetryConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Full jitter: the wait is uniform over `[0, min(cap, base · 2^n))`. Picking the exponential value
|
|
13
|
+
* itself synchronizes every client that failed at the same moment, which is the stampede the
|
|
14
|
+
* backoff exists to avoid.
|
|
15
|
+
*
|
|
16
|
+
* `attempt` is the attempt that just failed, counting from 1.
|
|
17
|
+
*/
|
|
18
|
+
export declare const backoffMs: (attempt: number, config: RetryConfig, random: () => number) => number;
|
|
19
|
+
/**
|
|
20
|
+
* The three outcomes where nothing came back, and so there is nothing to classify.
|
|
21
|
+
*
|
|
22
|
+
* **This says the attempt failed without an answer — never that the operation is safe to repeat.**
|
|
23
|
+
* A request that died after leaving may well have been carried out, which is why a write gets
|
|
24
|
+
* `outcome_unknown` rather than a retry.
|
|
25
|
+
*/
|
|
26
|
+
export declare const isTransportFailure: (code: ErrorCode) => boolean;
|
|
27
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,wFAAwF;IACxF,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,eAAO,MAAM,aAAa,EAAE,WAK3B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,YAAa,MAAM,UAAU,WAAW,UAAU,MAAM,MAAM,KAAG,MAGtF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,SAAU,SAAS,KAAG,OACmB,CAAA"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const DEFAULT_RETRY = {
|
|
2
|
+
retries: 1,
|
|
3
|
+
baseDelayMs: 250,
|
|
4
|
+
maxDelayMs: 10_000,
|
|
5
|
+
maxRetryAfterMs: 30_000,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Full jitter: the wait is uniform over `[0, min(cap, base · 2^n))`. Picking the exponential value
|
|
9
|
+
* itself synchronizes every client that failed at the same moment, which is the stampede the
|
|
10
|
+
* backoff exists to avoid.
|
|
11
|
+
*
|
|
12
|
+
* `attempt` is the attempt that just failed, counting from 1.
|
|
13
|
+
*/
|
|
14
|
+
export const backoffMs = (attempt, config, random) => {
|
|
15
|
+
const ceiling = Math.min(config.maxDelayMs, config.baseDelayMs * 2 ** (attempt - 1));
|
|
16
|
+
return Math.round(random() * ceiling);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The three outcomes where nothing came back, and so there is nothing to classify.
|
|
20
|
+
*
|
|
21
|
+
* **This says the attempt failed without an answer — never that the operation is safe to repeat.**
|
|
22
|
+
* A request that died after leaving may well have been carried out, which is why a write gets
|
|
23
|
+
* `outcome_unknown` rather than a retry.
|
|
24
|
+
*/
|
|
25
|
+
export const isTransportFailure = (code) => code === "timeout" || code === "network_error" || code === "cancelled";
|
|
26
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,MAAM;IAClB,eAAe,EAAE,MAAM;CACxB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,MAAmB,EAAE,MAAoB,EAAU,EAAE;IAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;IACpF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;AACvC,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAe,EAAW,EAAE,CAC7D,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,WAAW,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text that came from Braze or from a customer's input file, made safe to display.
|
|
3
|
+
*
|
|
4
|
+
* A campaign name, a catalog title and a user attribute are all edited somewhere we do not
|
|
5
|
+
* control and handed back to us as data. A terminal executes what it is given: `\x1b[2K\x1b[1G`
|
|
6
|
+
* clears the line and returns the cursor, so a campaign name can overwrite output this tool
|
|
7
|
+
* already printed. Braze's own error messages reach a terminal the same way (`SEC-2`).
|
|
8
|
+
*
|
|
9
|
+
* CLI machine output preserves original strings as a contract with scripts. JSON escapes C0
|
|
10
|
+
* characters but leaves C1 and other invisible Unicode raw; display and agent text need this guard.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Control characters become visible rather than disappearing. A reader has to know the value
|
|
14
|
+
* contained something strange; an audit that silently drops bytes is worse than one that shows
|
|
15
|
+
* them.
|
|
16
|
+
*
|
|
17
|
+
* `\t` and `\n` survive, because a table and a multi-line field are built out of them.
|
|
18
|
+
*/
|
|
19
|
+
export declare const visibleControls: (text: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* For a value that must stay on one line: a name, a title, a file name, a URL. A newline in a
|
|
22
|
+
* sender's name would otherwise print as a line of its own, indistinguishable from real output.
|
|
23
|
+
*/
|
|
24
|
+
export declare const singleLine: (text: string) => string;
|
|
25
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAwBH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,MAQ9C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAA6D,CAAA"}
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text that came from Braze or from a customer's input file, made safe to display.
|
|
3
|
+
*
|
|
4
|
+
* A campaign name, a catalog title and a user attribute are all edited somewhere we do not
|
|
5
|
+
* control and handed back to us as data. A terminal executes what it is given: `\x1b[2K\x1b[1G`
|
|
6
|
+
* clears the line and returns the cursor, so a campaign name can overwrite output this tool
|
|
7
|
+
* already printed. Braze's own error messages reach a terminal the same way (`SEC-2`).
|
|
8
|
+
*
|
|
9
|
+
* CLI machine output preserves original strings as a contract with scripts. JSON escapes C0
|
|
10
|
+
* characters but leaves C1 and other invisible Unicode raw; display and agent text need this guard.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* C0 minus tab and newline, DEL, and C1 — which xterm honours as escapes in its 8-bit form. Then
|
|
14
|
+
* the direction overrides and isolates, which reorder what follows them (`invoice\u202efdp.exe`
|
|
15
|
+
* reads as a PDF), and the invisible zero-width space and direction marks.
|
|
16
|
+
*
|
|
17
|
+
* U+200C and U+200D stay: Persian and Indic text need the non-joiner, and every multi-person emoji
|
|
18
|
+
* is built with the joiner.
|
|
19
|
+
*/
|
|
20
|
+
const CONTROL =
|
|
21
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching them is the whole point
|
|
22
|
+
/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u200B\u200E\u200F\u202A-\u202E\u2066-\u2069\uFEFF\u{E0000}-\u{E007F}]/gu;
|
|
23
|
+
const SUBDIVISION_FLAG = /\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}/gu;
|
|
24
|
+
const LINE_BREAKS = /[\t\n\u2028\u2029]/g;
|
|
25
|
+
const escaped = (c) => {
|
|
26
|
+
const code = c.codePointAt(0) ?? 0;
|
|
27
|
+
if (code > 0xffff)
|
|
28
|
+
return `\\u{${code.toString(16)}}`;
|
|
29
|
+
return code > 0xff ? `\\u${code.toString(16).padStart(4, "0")}` : `\\x${code.toString(16).padStart(2, "0")}`;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Control characters become visible rather than disappearing. A reader has to know the value
|
|
33
|
+
* contained something strange; an audit that silently drops bytes is worse than one that shows
|
|
34
|
+
* them.
|
|
35
|
+
*
|
|
36
|
+
* `\t` and `\n` survive, because a table and a multi-line field are built out of them.
|
|
37
|
+
*/
|
|
38
|
+
export const visibleControls = (text) => {
|
|
39
|
+
let at = 0;
|
|
40
|
+
let result = "";
|
|
41
|
+
for (const flag of text.matchAll(SUBDIVISION_FLAG)) {
|
|
42
|
+
result += text.slice(at, flag.index).replace(CONTROL, escaped) + flag[0];
|
|
43
|
+
at = flag.index + flag[0].length;
|
|
44
|
+
}
|
|
45
|
+
return result + text.slice(at).replace(CONTROL, escaped);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* For a value that must stay on one line: a name, a title, a file name, a URL. A newline in a
|
|
49
|
+
* sender's name would otherwise print as a line of its own, indistinguishable from real output.
|
|
50
|
+
*/
|
|
51
|
+
export const singleLine = (text) => visibleControls(text).replace(LINE_BREAKS, escaped);
|
|
52
|
+
//# sourceMappingURL=sanitize.js.map
|