@ploaness/governance 1.0.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 +21 -0
- package/README.md +13 -0
- package/dist/access-boundary.d.ts +41 -0
- package/dist/access-boundary.d.ts.map +1 -0
- package/dist/access-boundary.js +26 -0
- package/dist/access-boundary.js.map +1 -0
- package/dist/agent-references.d.ts +12 -0
- package/dist/agent-references.d.ts.map +1 -0
- package/dist/agent-references.js +53 -0
- package/dist/agent-references.js.map +1 -0
- package/dist/architecture-layers.d.ts +11 -0
- package/dist/architecture-layers.d.ts.map +1 -0
- package/dist/architecture-layers.js +47 -0
- package/dist/architecture-layers.js.map +1 -0
- package/dist/asset-policy.d.ts +138 -0
- package/dist/asset-policy.d.ts.map +1 -0
- package/dist/asset-policy.js +312 -0
- package/dist/asset-policy.js.map +1 -0
- package/dist/banned-typography.d.ts +13 -0
- package/dist/banned-typography.d.ts.map +1 -0
- package/dist/banned-typography.js +78 -0
- package/dist/banned-typography.js.map +1 -0
- package/dist/bundle-budget.d.ts +16 -0
- package/dist/bundle-budget.d.ts.map +1 -0
- package/dist/bundle-budget.js +37 -0
- package/dist/bundle-budget.js.map +1 -0
- package/dist/commit-message.d.ts +37 -0
- package/dist/commit-message.d.ts.map +1 -0
- package/dist/commit-message.js +144 -0
- package/dist/commit-message.js.map +1 -0
- package/dist/config-references.d.ts +17 -0
- package/dist/config-references.d.ts.map +1 -0
- package/dist/config-references.js +54 -0
- package/dist/config-references.js.map +1 -0
- package/dist/container-failure.d.ts +40 -0
- package/dist/container-failure.d.ts.map +1 -0
- package/dist/container-failure.js +154 -0
- package/dist/container-failure.js.map +1 -0
- package/dist/container-files.d.ts +36 -0
- package/dist/container-files.d.ts.map +1 -0
- package/dist/container-files.js +74 -0
- package/dist/container-files.js.map +1 -0
- package/dist/container-freshness.d.ts +102 -0
- package/dist/container-freshness.d.ts.map +1 -0
- package/dist/container-freshness.js +129 -0
- package/dist/container-freshness.js.map +1 -0
- package/dist/dependency-freshness.d.ts +129 -0
- package/dist/dependency-freshness.d.ts.map +1 -0
- package/dist/dependency-freshness.js +161 -0
- package/dist/dependency-freshness.js.map +1 -0
- package/dist/doc-blocks.d.ts +13 -0
- package/dist/doc-blocks.d.ts.map +1 -0
- package/dist/doc-blocks.js +75 -0
- package/dist/doc-blocks.js.map +1 -0
- package/dist/document-references.d.ts +44 -0
- package/dist/document-references.d.ts.map +1 -0
- package/dist/document-references.js +94 -0
- package/dist/document-references.js.map +1 -0
- package/dist/editorconfig-policy.d.ts +24 -0
- package/dist/editorconfig-policy.d.ts.map +1 -0
- package/dist/editorconfig-policy.js +106 -0
- package/dist/editorconfig-policy.js.map +1 -0
- package/dist/environment-files.d.ts +42 -0
- package/dist/environment-files.d.ts.map +1 -0
- package/dist/environment-files.js +76 -0
- package/dist/environment-files.js.map +1 -0
- package/dist/file-roles.d.ts +39 -0
- package/dist/file-roles.d.ts.map +1 -0
- package/dist/file-roles.js +82 -0
- package/dist/file-roles.js.map +1 -0
- package/dist/generated-denial.d.ts +42 -0
- package/dist/generated-denial.d.ts.map +1 -0
- package/dist/generated-denial.js +87 -0
- package/dist/generated-denial.js.map +1 -0
- package/dist/history-policy.d.ts +32 -0
- package/dist/history-policy.d.ts.map +1 -0
- package/dist/history-policy.js +50 -0
- package/dist/history-policy.js.map +1 -0
- package/dist/image-assets.d.ts +14 -0
- package/dist/image-assets.d.ts.map +1 -0
- package/dist/image-assets.js +205 -0
- package/dist/image-assets.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/install-policy.d.ts +35 -0
- package/dist/install-policy.d.ts.map +1 -0
- package/dist/install-policy.js +61 -0
- package/dist/install-policy.js.map +1 -0
- package/dist/json-shapes.d.ts +86 -0
- package/dist/json-shapes.d.ts.map +1 -0
- package/dist/json-shapes.js +124 -0
- package/dist/json-shapes.js.map +1 -0
- package/dist/license-allowlist.d.ts +15 -0
- package/dist/license-allowlist.d.ts.map +1 -0
- package/dist/license-allowlist.js +157 -0
- package/dist/license-allowlist.js.map +1 -0
- package/dist/network-policy.d.ts +69 -0
- package/dist/network-policy.d.ts.map +1 -0
- package/dist/network-policy.js +173 -0
- package/dist/network-policy.js.map +1 -0
- package/dist/paired-assets.d.ts +34 -0
- package/dist/paired-assets.d.ts.map +1 -0
- package/dist/paired-assets.js +63 -0
- package/dist/paired-assets.js.map +1 -0
- package/dist/payload-access.d.ts +9 -0
- package/dist/payload-access.d.ts.map +1 -0
- package/dist/payload-access.js +168 -0
- package/dist/payload-access.js.map +1 -0
- package/dist/payload-policy.d.ts +18 -0
- package/dist/payload-policy.d.ts.map +1 -0
- package/dist/payload-policy.js +124 -0
- package/dist/payload-policy.js.map +1 -0
- package/dist/payload-source.d.ts +29 -0
- package/dist/payload-source.d.ts.map +1 -0
- package/dist/payload-source.js +84 -0
- package/dist/payload-source.js.map +1 -0
- package/dist/release-age.d.ts +23 -0
- package/dist/release-age.d.ts.map +1 -0
- package/dist/release-age.js +38 -0
- package/dist/release-age.js.map +1 -0
- package/dist/request-limits.d.ts +39 -0
- package/dist/request-limits.d.ts.map +1 -0
- package/dist/request-limits.js +57 -0
- package/dist/request-limits.js.map +1 -0
- package/dist/run-plan.d.ts +32 -0
- package/dist/run-plan.d.ts.map +1 -0
- package/dist/run-plan.js +43 -0
- package/dist/run-plan.js.map +1 -0
- package/dist/run-policy.d.ts +32 -0
- package/dist/run-policy.d.ts.map +1 -0
- package/dist/run-policy.js +37 -0
- package/dist/run-policy.js.map +1 -0
- package/dist/secret-policy.d.ts +13 -0
- package/dist/secret-policy.d.ts.map +1 -0
- package/dist/secret-policy.js +69 -0
- package/dist/secret-policy.js.map +1 -0
- package/dist/settings-layering.d.ts +45 -0
- package/dist/settings-layering.d.ts.map +1 -0
- package/dist/settings-layering.js +134 -0
- package/dist/settings-layering.js.map +1 -0
- package/dist/settings.d.ts +238 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +378 -0
- package/dist/settings.js.map +1 -0
- package/dist/skill-manifest.d.ts +18 -0
- package/dist/skill-manifest.d.ts.map +1 -0
- package/dist/skill-manifest.js +125 -0
- package/dist/skill-manifest.js.map +1 -0
- package/dist/source-text.d.ts +83 -0
- package/dist/source-text.d.ts.map +1 -0
- package/dist/source-text.js +310 -0
- package/dist/source-text.js.map +1 -0
- package/dist/suppression-ceiling.d.ts +48 -0
- package/dist/suppression-ceiling.d.ts.map +1 -0
- package/dist/suppression-ceiling.js +115 -0
- package/dist/suppression-ceiling.js.map +1 -0
- package/dist/tailwind-tokens.d.ts +16 -0
- package/dist/tailwind-tokens.d.ts.map +1 -0
- package/dist/tailwind-tokens.js +70 -0
- package/dist/tailwind-tokens.js.map +1 -0
- package/dist/text-escapes.d.ts +7 -0
- package/dist/text-escapes.d.ts.map +1 -0
- package/dist/text-escapes.js +14 -0
- package/dist/text-escapes.js.map +1 -0
- package/dist/toolchain-pins.d.ts +42 -0
- package/dist/toolchain-pins.d.ts.map +1 -0
- package/dist/toolchain-pins.js +99 -0
- package/dist/toolchain-pins.js.map +1 -0
- package/dist/url-safety.d.ts +12 -0
- package/dist/url-safety.d.ts.map +1 -0
- package/dist/url-safety.js +58 -0
- package/dist/url-safety.js.map +1 -0
- package/dist/version-policy.d.ts +99 -0
- package/dist/version-policy.d.ts.map +1 -0
- package/dist/version-policy.js +306 -0
- package/dist/version-policy.js.map +1 -0
- package/dist/vulnerability-policy.d.ts +66 -0
- package/dist/vulnerability-policy.d.ts.map +1 -0
- package/dist/vulnerability-policy.js +106 -0
- package/dist/vulnerability-policy.js.map +1 -0
- package/dist/wiring-policy.d.ts +226 -0
- package/dist/wiring-policy.d.ts.map +1 -0
- package/dist/wiring-policy.js +458 -0
- package/dist/wiring-policy.js.map +1 -0
- package/dist/wiring-violation.d.ts +6 -0
- package/dist/wiring-violation.d.ts.map +1 -0
- package/dist/wiring-violation.js +8 -0
- package/dist/wiring-violation.js.map +1 -0
- package/dist/workspace-policy.d.ts +177 -0
- package/dist/workspace-policy.d.ts.map +1 -0
- package/dist/workspace-policy.js +228 -0
- package/dist/workspace-policy.js.map +1 -0
- package/dist/yaml-blocks.d.ts +43 -0
- package/dist/yaml-blocks.d.ts.map +1 -0
- package/dist/yaml-blocks.js +87 -0
- package/dist/yaml-blocks.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// Managed-file policy. Some configuration is read
|
|
2
|
+
// from the working tree by tools ploaness does not run (git, editors, coding agents), so it cannot live
|
|
3
|
+
// inside the package and be resolved through node_modules the way an ESLint or Biome config can. Those
|
|
4
|
+
// files are materialised into the consumer tree by `ploaness sync` and policed here.
|
|
5
|
+
//
|
|
6
|
+
// Five dispositions:
|
|
7
|
+
// PINNED - ploaness owns the content; the file must exist and match byte for byte.
|
|
8
|
+
// SEED - ploaness writes it once when absent; the project owns it thereafter and may edit it.
|
|
9
|
+
// FORBIDDEN - the path must not exist, because ploaness supplies that configuration itself and a
|
|
10
|
+
// working-tree copy would silently shadow or contradict it.
|
|
11
|
+
// SECTION - ploaness owns a marked block at the top of the file; the project owns everything below
|
|
12
|
+
// it. Neither PINNED nor SEED fits a file both parties must write: pinning it would forbid
|
|
13
|
+
// the project its own agent instructions, and seeding it would let the contract statement
|
|
14
|
+
// drift the moment ploaness changed.
|
|
15
|
+
// REFERENCE - the path need not exist, but if it does it carries nothing but a pointer at AGENTS.md.
|
|
16
|
+
// This is for the entry-point file a coding agent other than Claude reads. PINNED cannot
|
|
17
|
+
// express it: pinning would force a project to carry an entry point for a tool it does
|
|
18
|
+
// not use, and FORBIDDEN would bar it from using that tool at all. What the rule refuses
|
|
19
|
+
// is a second set of instructions - the standard says the root instruction file holds the
|
|
20
|
+
// rules and a tool-specific one only points at it, so two agents cannot be told two
|
|
21
|
+
// different things. The form of the pointer is the tool's own: `@AGENTS.md` is Claude's
|
|
22
|
+
// import syntax and means nothing to Cursor, so the check reads for the NAME rather than
|
|
23
|
+
// demanding one spelling and forcing a file the tool cannot act on.
|
|
24
|
+
const DISPOSITIONS = new Set([
|
|
25
|
+
'PINNED',
|
|
26
|
+
'SEED',
|
|
27
|
+
'FORBIDDEN',
|
|
28
|
+
'SECTION',
|
|
29
|
+
'REFERENCE',
|
|
30
|
+
]);
|
|
31
|
+
// A guard rather than a set membership test followed by an assertion. Both say the same thing; only
|
|
32
|
+
// this one is a claim the compiler checks, so a disposition added to the union without being added to
|
|
33
|
+
// the set above stops compiling instead of parsing into a value no rule handles.
|
|
34
|
+
const isDisposition = (raw) => DISPOSITIONS.has(raw);
|
|
35
|
+
const SCOPES = new Set([
|
|
36
|
+
'REPOSITORY',
|
|
37
|
+
'APPLICATION',
|
|
38
|
+
'PAYLOAD',
|
|
39
|
+
'EVERYWHERE',
|
|
40
|
+
]);
|
|
41
|
+
const isScope = (raw) => SCOPES.has(raw);
|
|
42
|
+
// A manifest row is a path, a disposition and a scope, and nothing else. A row missing the third column
|
|
43
|
+
// is malformed rather than defaulted: a managed file placed in the wrong half of a workspace is either
|
|
44
|
+
// demanded where it cannot be or ignored where it must be, and neither should be reachable by omission.
|
|
45
|
+
const MANIFEST_COLUMNS = 3;
|
|
46
|
+
/** Opens the block ploaness owns inside a SECTION file. */
|
|
47
|
+
export const SECTION_BEGIN = '<!-- BEGIN PLOANESS MANAGED INSTRUCTIONS -->';
|
|
48
|
+
/** Closes the block ploaness owns inside a SECTION file. */
|
|
49
|
+
export const SECTION_END = '<!-- END PLOANESS MANAGED INSTRUCTIONS -->';
|
|
50
|
+
const occurrences = (content, marker) => content.split(marker).length - 1;
|
|
51
|
+
/**
|
|
52
|
+
* Read back the ploaness-owned block, markers included.
|
|
53
|
+
*
|
|
54
|
+
* The markers are HTML comments so they survive in every Markdown renderer without being visible to a
|
|
55
|
+
* human reader, and so an agent editing the file below them can see exactly where its own territory
|
|
56
|
+
* starts. The block must lead the file and appear exactly once: a contract an agent reaches only after
|
|
57
|
+
* scrolling past the project's own prose is not the first thing it reads, and a second copy would let
|
|
58
|
+
* one block drift while the other stayed current.
|
|
59
|
+
* @param rawContent the whole file as it currently stands in the working tree.
|
|
60
|
+
* @returns which of the three states the file is in.
|
|
61
|
+
*/
|
|
62
|
+
export const readManagedSection = (rawContent) => {
|
|
63
|
+
// Line endings are normalised before the markers are looked for. The begin marker is required to be
|
|
64
|
+
// followed by `\n`, so on a CRLF checkout every SECTION file reported "the managed block does not
|
|
65
|
+
// begin the file" and told the reader to repair markers that were already correct.
|
|
66
|
+
const content = rawContent.replaceAll('\r\n', '\n');
|
|
67
|
+
const begins = occurrences(content, SECTION_BEGIN);
|
|
68
|
+
const ends = occurrences(content, SECTION_END);
|
|
69
|
+
if (begins === 0 && ends === 0) {
|
|
70
|
+
return { kind: 'absent' };
|
|
71
|
+
}
|
|
72
|
+
if (begins !== 1 || ends !== 1) {
|
|
73
|
+
return {
|
|
74
|
+
kind: 'malformed',
|
|
75
|
+
reason: 'the managed markers appear more than once, or one is missing',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Requiring the block to lead the file also settles marker order: with one of each marker and the
|
|
79
|
+
// begin marker at offset zero, the end marker can only follow it.
|
|
80
|
+
if (!content.startsWith(`${SECTION_BEGIN}\n`)) {
|
|
81
|
+
return { kind: 'malformed', reason: 'the managed block does not begin the file' };
|
|
82
|
+
}
|
|
83
|
+
const end = content.indexOf(SECTION_END);
|
|
84
|
+
return { kind: 'present', block: content.slice(0, end + SECTION_END.length) };
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Put the ploaness-owned block into a file, preserving everything the project owns.
|
|
88
|
+
*
|
|
89
|
+
* An existing block is replaced where it stands, so the project text below it is carried through
|
|
90
|
+
* byte for byte. A file with no block gets one at the very top, because the contract has to be the
|
|
91
|
+
* first thing an agent reads.
|
|
92
|
+
* @param rawContent the current file, or the empty string when it does not exist yet.
|
|
93
|
+
* @param block the block ploaness ships, markers included.
|
|
94
|
+
* @returns the spliced file, or undefined when the markers are too ambiguous to edit safely.
|
|
95
|
+
*/
|
|
96
|
+
export const applyManagedSection = (rawContent, block) => {
|
|
97
|
+
// Normalised here as well, and for a sharper reason than in the reader: the block length below is
|
|
98
|
+
// measured on the normalised text, so slicing the raw one would cut at the wrong offset by exactly
|
|
99
|
+
// the number of carriage returns the block contained.
|
|
100
|
+
const content = rawContent.replaceAll('\r\n', '\n');
|
|
101
|
+
const state = readManagedSection(content);
|
|
102
|
+
if (state.kind === 'malformed') {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
if (state.kind === 'present') {
|
|
106
|
+
return block + content.slice(state.block.length);
|
|
107
|
+
}
|
|
108
|
+
return content.trim().length === 0 ? `${block}\n` : `${block}\n\n${content.replace(/^\n+/, '')}`;
|
|
109
|
+
};
|
|
110
|
+
const readManifestRow = (index, line) => {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
if (trimmed.length === 0 || trimmed.startsWith('#')) {
|
|
113
|
+
return { asset: undefined, problem: undefined };
|
|
114
|
+
}
|
|
115
|
+
const [path, disposition, scope] = trimmed.split('\t', MANIFEST_COLUMNS);
|
|
116
|
+
if (path === undefined ||
|
|
117
|
+
disposition === undefined ||
|
|
118
|
+
scope === undefined ||
|
|
119
|
+
!isDisposition(disposition) ||
|
|
120
|
+
!isScope(scope)) {
|
|
121
|
+
return {
|
|
122
|
+
asset: undefined,
|
|
123
|
+
// Rendered from the sets rather than written out. The list was spelled a third time here and had
|
|
124
|
+
// already drifted: REFERENCE was missing, so a typo in a REFERENCE row produced advice that
|
|
125
|
+
// contradicted the set the parser actually accepts.
|
|
126
|
+
problem: `manifest line ${String(index + 1)}: expected "<path>", a tab, one of ` +
|
|
127
|
+
`${[...DISPOSITIONS].join(', ')}, a tab, then one of ${[...SCOPES].join(', ')}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return { asset: { path, disposition, scope }, problem: undefined };
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Parse a manifest into catalogue entries. Blank lines and `#` comments are ignored; a row is a
|
|
134
|
+
* repo-relative path, a tab, and a disposition. A malformed row is reported rather than silently
|
|
135
|
+
* skipped, so a packaging mistake cannot quietly shrink the catalogue.
|
|
136
|
+
* @param manifest the raw manifest.tsv contents.
|
|
137
|
+
* @returns the parsed entries and any malformed rows.
|
|
138
|
+
*/
|
|
139
|
+
export const parseManifest = (manifest) => {
|
|
140
|
+
// Each row is read once into an outcome, then the outcomes are partitioned. A row that is neither an
|
|
141
|
+
// entry nor a problem is a comment or a blank line, and simply yields nothing.
|
|
142
|
+
const rows = [...manifest.split('\n').entries()]
|
|
143
|
+
.map(([index, line]) => readManifestRow(index, line))
|
|
144
|
+
.filter((row) => row.asset !== undefined || row.problem !== undefined);
|
|
145
|
+
return {
|
|
146
|
+
assets: rows.flatMap((row) => row.asset === undefined ? [] : [row.asset]),
|
|
147
|
+
problems: rows.flatMap((row) => row.problem === undefined ? [] : [row.problem]),
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Judge a path whose marked block ploaness owns and whose remaining text the project owns.
|
|
152
|
+
* @param asset the catalogue entry.
|
|
153
|
+
* @param state the working-tree and shipped content for that path.
|
|
154
|
+
* @returns a violation, or undefined when the block matches what ploaness ships.
|
|
155
|
+
*/
|
|
156
|
+
const checkSection = (asset, state) => {
|
|
157
|
+
const section = readManagedSection(state.actual ?? '');
|
|
158
|
+
// Malformed markers get their own advice on purpose: `ploaness sync` refuses such a file, so telling
|
|
159
|
+
// the project to run it would send it round a loop that cannot terminate.
|
|
160
|
+
if (section.kind === 'malformed') {
|
|
161
|
+
return { path: asset.path, reason: `${section.reason}; repair the markers by hand` };
|
|
162
|
+
}
|
|
163
|
+
if (section.kind === 'absent') {
|
|
164
|
+
return {
|
|
165
|
+
path: asset.path,
|
|
166
|
+
reason: 'the ploaness managed block is missing; run `ploaness sync`',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return section.block === (state.expected ?? '').trim()
|
|
170
|
+
? undefined
|
|
171
|
+
: {
|
|
172
|
+
path: asset.path,
|
|
173
|
+
reason: 'the ploaness managed block drifted from the ploaness copy; run `ploaness sync`',
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
// One checker per disposition, rather than a chain of tests inside `checkAsset`. Five dispositions
|
|
177
|
+
// carried that chain past the complexity cap, and the cap is never raised - so the shape changed. Each
|
|
178
|
+
// checker decides the absent case for itself, because that is precisely where the dispositions differ:
|
|
179
|
+
// a missing PINNED file is a defect, a missing REFERENCE file is a project that uses one agent.
|
|
180
|
+
const missing = (asset) => ({
|
|
181
|
+
path: asset.path,
|
|
182
|
+
reason: 'managed file is missing; run `ploaness sync`',
|
|
183
|
+
});
|
|
184
|
+
const checkForbidden = (asset, state) => state.isPresent
|
|
185
|
+
? {
|
|
186
|
+
path: asset.path,
|
|
187
|
+
reason: 'ploaness supplies this configuration, so a working-tree copy shadows it; delete the file',
|
|
188
|
+
}
|
|
189
|
+
: undefined;
|
|
190
|
+
const checkSeed = (asset, state) => state.isPresent ? undefined : missing(asset);
|
|
191
|
+
const checkPinned = (asset, state) => {
|
|
192
|
+
if (!state.isPresent) {
|
|
193
|
+
return missing(asset);
|
|
194
|
+
}
|
|
195
|
+
return state.actual === state.expected
|
|
196
|
+
? undefined
|
|
197
|
+
: {
|
|
198
|
+
path: asset.path,
|
|
199
|
+
reason: 'managed file drifted from the ploaness copy; run `ploaness sync`',
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
/** The instruction file every other one points at. */
|
|
203
|
+
const ROOT_INSTRUCTION_FILE = 'AGENTS.md';
|
|
204
|
+
// Every line that says anything must say the same thing: read AGENTS.md. A line that does not name it
|
|
205
|
+
// is this file stating a rule of its own, which is what makes two agents answerable to two contracts.
|
|
206
|
+
const checkReference = (asset, state) => {
|
|
207
|
+
// Optional, unlike every other disposition: a project that uses no coding agent but Claude carries no
|
|
208
|
+
// entry point for one, and requiring the file would be ploaness deciding which tools it uses.
|
|
209
|
+
if (!state.isPresent) {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
const stated = (state.actual ?? '')
|
|
213
|
+
.split('\n')
|
|
214
|
+
.map((line) => line.trim())
|
|
215
|
+
.filter((line) => line.length > 0);
|
|
216
|
+
if (stated.length === 0) {
|
|
217
|
+
return {
|
|
218
|
+
path: asset.path,
|
|
219
|
+
reason: `is empty; a tool-specific instruction file points at ${ROOT_INSTRUCTION_FILE}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return stated.every((line) => line.includes(ROOT_INSTRUCTION_FILE))
|
|
223
|
+
? undefined
|
|
224
|
+
: {
|
|
225
|
+
path: asset.path,
|
|
226
|
+
reason: `states instructions of its own; a tool-specific instruction file carries nothing but a ` +
|
|
227
|
+
`reference to ${ROOT_INSTRUCTION_FILE}, so no two agents are held to two contracts`,
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
const CHECKERS = {
|
|
231
|
+
FORBIDDEN: checkForbidden,
|
|
232
|
+
PINNED: checkPinned,
|
|
233
|
+
REFERENCE: checkReference,
|
|
234
|
+
SECTION: (asset, state) => state.isPresent ? checkSection(asset, state) : missing(asset),
|
|
235
|
+
SEED: checkSeed,
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Judge one managed path against its disposition.
|
|
239
|
+
* @param asset the catalogue entry.
|
|
240
|
+
* @param state the working-tree facts for that path.
|
|
241
|
+
* @returns a violation, or undefined when the path conforms.
|
|
242
|
+
*/
|
|
243
|
+
export const checkAsset = (asset, state) => CHECKERS[asset.disposition](asset, state);
|
|
244
|
+
/**
|
|
245
|
+
* Judge every catalogue entry the project has not explicitly taken over.
|
|
246
|
+
* @param assets the parsed catalogue.
|
|
247
|
+
* @param unmanaged repo-relative paths the project owns instead.
|
|
248
|
+
* @param stateOf resolves the working-tree and shipped content for a path.
|
|
249
|
+
* @returns one violation per non-conforming path; empty means the tree matches the catalogue.
|
|
250
|
+
*/
|
|
251
|
+
export const findAssetViolations = (assets, unmanaged, stateOf) => {
|
|
252
|
+
const owned = new Set(unmanaged);
|
|
253
|
+
return assets.flatMap((asset) => {
|
|
254
|
+
if (owned.has(asset.path)) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
const violation = checkAsset(asset, stateOf(asset.path));
|
|
258
|
+
return violation === undefined ? [] : [violation];
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Decide the sync action for one path. A PINNED file is always rewritten, so drift is repaired; a SEED
|
|
263
|
+
* file is written only when absent, so the project's own edits survive; a FORBIDDEN file is deleted; a
|
|
264
|
+
* SECTION file has only its marked block replaced.
|
|
265
|
+
* @param asset the catalogue entry.
|
|
266
|
+
* @param isPresent whether the path is currently present in the working tree.
|
|
267
|
+
* @returns the action `ploaness sync` performs.
|
|
268
|
+
*/
|
|
269
|
+
export const syncAction = (asset, isPresent) => {
|
|
270
|
+
if (asset.disposition === 'FORBIDDEN') {
|
|
271
|
+
return isPresent ? 'delete' : 'skip';
|
|
272
|
+
}
|
|
273
|
+
// Never written, in either state. Creating one would hand the project an entry point for a tool it
|
|
274
|
+
// may not use, and rewriting one would replace a pointer the tool understands with a guess.
|
|
275
|
+
if (asset.disposition === 'REFERENCE') {
|
|
276
|
+
return 'skip';
|
|
277
|
+
}
|
|
278
|
+
if (asset.disposition === 'SEED') {
|
|
279
|
+
return isPresent ? 'skip' : 'write';
|
|
280
|
+
}
|
|
281
|
+
// A SECTION file is never overwritten, even when absent: the project text below the block is the
|
|
282
|
+
// project's, and creating the file wholesale would be ploaness writing that text on its behalf.
|
|
283
|
+
if (asset.disposition === 'SECTION') {
|
|
284
|
+
return 'splice';
|
|
285
|
+
}
|
|
286
|
+
return 'write';
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* The catalogue entries that apply at the repository root.
|
|
290
|
+
* @param assets the whole catalogue.
|
|
291
|
+
* @returns the repository-scope entries and the forbidden paths, which apply everywhere.
|
|
292
|
+
*/
|
|
293
|
+
export const repositoryAssets = (assets) => assets.filter((asset) => asset.scope === 'REPOSITORY' || asset.scope === 'EVERYWHERE');
|
|
294
|
+
/**
|
|
295
|
+
* The catalogue entries that apply inside one member.
|
|
296
|
+
*
|
|
297
|
+
* A library receives only the forbidden paths: it has no server to sweep, so a spec that drives one
|
|
298
|
+
* would be a managed file demanding a browser the package was never going to open.
|
|
299
|
+
* @param assets the whole catalogue.
|
|
300
|
+
* @param host what the member can hold, derived from its kind.
|
|
301
|
+
* @returns the entries that apply to that member.
|
|
302
|
+
*/
|
|
303
|
+
export const memberAssets = (assets, host) => assets.filter((asset) => {
|
|
304
|
+
if (asset.scope === 'EVERYWHERE') {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (host.isPayload && asset.scope === 'PAYLOAD') {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return host.hasRuntime && asset.scope === 'APPLICATION';
|
|
311
|
+
});
|
|
312
|
+
//# sourceMappingURL=asset-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-policy.js","sourceRoot":"","sources":["../src/asset-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TypographyViolation {
|
|
2
|
+
readonly line: number;
|
|
3
|
+
readonly column: number;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly replacement: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Scans text for banned AI-typography, reporting 1-based line and column positions.
|
|
9
|
+
* @param text the content to scan, with lines separated by "\n" or "\r\n".
|
|
10
|
+
* @returns one violation per banned character found, in reading order.
|
|
11
|
+
*/
|
|
12
|
+
export declare const findTypographyViolations: (text: string) => readonly TypographyViolation[];
|
|
13
|
+
//# sourceMappingURL=banned-typography.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banned-typography.d.ts","sourceRoot":"","sources":["../src/banned-typography.ts"],"names":[],"mappings":"AAmDA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAiCD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,KAAG,SAAS,mBAAmB,EAM/E,CAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Shared AI-typography ban: characters a human rarely types but language models love, each mapped to
|
|
2
|
+
// the plain ASCII a human would actually write. One source of truth, consumed by both the file gate
|
|
3
|
+
// (`conventions`, in packages/cli/src/checks/conventions.ts) and the history gate (`commit-history`, in
|
|
4
|
+
// packages/cli/src/checks/history.ts). Every banned character is
|
|
5
|
+
// referenced via its code point only, so neither this module nor its callers trip the check on
|
|
6
|
+
// themselves.
|
|
7
|
+
const STRAIGHT_DOUBLE_QUOTE = 'a straight double quote';
|
|
8
|
+
const PLAIN_HYPHEN = 'a hyphen "-"';
|
|
9
|
+
// The code points are named rather than written at each use, because a bare number in the table below
|
|
10
|
+
// would be exactly the unexplained literal the standard bans - and this module cannot spell the
|
|
11
|
+
// characters out, since its own source is scanned by the rule it implements.
|
|
12
|
+
const EM_DASH = 0x20_14;
|
|
13
|
+
const EN_DASH = 0x20_13;
|
|
14
|
+
const ELLIPSIS = 0x20_26;
|
|
15
|
+
const LEFT_DOUBLE_QUOTE = 0x20_1c;
|
|
16
|
+
const RIGHT_DOUBLE_QUOTE = 0x20_1d;
|
|
17
|
+
const LOW_DOUBLE_QUOTE = 0x20_1e;
|
|
18
|
+
const BANNED_CHARACTERS = [
|
|
19
|
+
{ char: String.fromCodePoint(EM_DASH), label: 'em dash (U+2014)', replacement: PLAIN_HYPHEN },
|
|
20
|
+
{ char: String.fromCodePoint(EN_DASH), label: 'en dash (U+2013)', replacement: PLAIN_HYPHEN },
|
|
21
|
+
{
|
|
22
|
+
char: String.fromCodePoint(ELLIPSIS),
|
|
23
|
+
label: 'ellipsis (U+2026)',
|
|
24
|
+
replacement: 'three dots "..."',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
char: String.fromCodePoint(LEFT_DOUBLE_QUOTE),
|
|
28
|
+
label: 'left double quote (U+201C)',
|
|
29
|
+
replacement: STRAIGHT_DOUBLE_QUOTE,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
char: String.fromCodePoint(RIGHT_DOUBLE_QUOTE),
|
|
33
|
+
label: 'right double quote (U+201D)',
|
|
34
|
+
replacement: STRAIGHT_DOUBLE_QUOTE,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
char: String.fromCodePoint(LOW_DOUBLE_QUOTE),
|
|
38
|
+
label: 'low double quote (U+201E)',
|
|
39
|
+
replacement: STRAIGHT_DOUBLE_QUOTE,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
const BY_CHARACTER = new Map(BANNED_CHARACTERS.map((banned) => [
|
|
43
|
+
banned.char,
|
|
44
|
+
banned,
|
|
45
|
+
]));
|
|
46
|
+
// Every occurrence, at its own column, in the order it is read.
|
|
47
|
+
//
|
|
48
|
+
// This used to run `indexOf` once per banned character, which reported only the FIRST of each on a
|
|
49
|
+
// line - so a line with two em dashes produced one finding, and clearing a file took as many runs as
|
|
50
|
+
// it had repeats. Walking the line by code point fixes the count and the column together: `indexOf`
|
|
51
|
+
// returns a UTF-16 offset, so a column after an emoji named a position the editor does not have.
|
|
52
|
+
const violationsInLine = (line, lineNumber) =>
|
|
53
|
+
// Code points are the unit wanted here: a banned character is one code point, and a column counted
|
|
54
|
+
// in code units names a position no editor agrees with.
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-spread -- see the note above
|
|
56
|
+
[...line].flatMap((character, index) => {
|
|
57
|
+
const banned = BY_CHARACTER.get(character);
|
|
58
|
+
return banned === undefined
|
|
59
|
+
? []
|
|
60
|
+
: [
|
|
61
|
+
{
|
|
62
|
+
line: lineNumber,
|
|
63
|
+
column: index + 1,
|
|
64
|
+
label: banned.label,
|
|
65
|
+
replacement: banned.replacement,
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Scans text for banned AI-typography, reporting 1-based line and column positions.
|
|
71
|
+
* @param text the content to scan, with lines separated by "\n" or "\r\n".
|
|
72
|
+
* @returns one violation per banned character found, in reading order.
|
|
73
|
+
*/
|
|
74
|
+
export const findTypographyViolations = (text) => text
|
|
75
|
+
.replaceAll('\r\n', '\n')
|
|
76
|
+
.split('\n')
|
|
77
|
+
.flatMap((line, index) => violationsInLine(line, index + 1));
|
|
78
|
+
//# sourceMappingURL=banned-typography.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banned-typography.js","sourceRoot":"","sources":["../src/banned-typography.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,oGAAoG;AACpG,wGAAwG;AACxG,iEAAiE;AACjE,+FAA+F;AAC/F,cAAc;AAQd,MAAM,qBAAqB,GAAW,yBAAyB,CAAA;AAC/D,MAAM,YAAY,GAAW,cAAc,CAAA;AAE3C,sGAAsG;AACtG,gGAAgG;AAChG,6EAA6E;AAC7E,MAAM,OAAO,GAAW,OAAO,CAAA;AAC/B,MAAM,OAAO,GAAW,OAAO,CAAA;AAC/B,MAAM,QAAQ,GAAW,OAAO,CAAA;AAChC,MAAM,iBAAiB,GAAW,OAAO,CAAA;AACzC,MAAM,kBAAkB,GAAW,OAAO,CAAA;AAC1C,MAAM,gBAAgB,GAAW,OAAO,CAAA;AAExC,MAAM,iBAAiB,GAA+B;IACpD,EAAE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE;IAC7F,EAAE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE;IAC7F;QACE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;QACpC,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,kBAAkB;KAChC;IACD;QACE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC;QAC7C,KAAK,EAAE,4BAA4B;QACnC,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC;QAC9C,KAAK,EAAE,6BAA6B;QACpC,WAAW,EAAE,qBAAqB;KACnC;IACD;QACE,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC;QAC5C,KAAK,EAAE,2BAA2B;QAClC,WAAW,EAAE,qBAAqB;KACnC;CACF,CAAA;AASD,MAAM,YAAY,GAAyC,IAAI,GAAG,CAChE,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAuB,EAAsC,EAAE,CAAC;IACrF,MAAM,CAAC,IAAI;IACX,MAAM;CACP,CAAC,CACH,CAAA;AAED,gEAAgE;AAChE,EAAE;AACF,mGAAmG;AACnG,qGAAqG;AACrG,oGAAoG;AACpG,iGAAiG;AACjG,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,UAAkB,EAAkC,EAAE;AAC5F,mGAAmG;AACnG,wDAAwD;AACxD,sFAAsF;AACtF,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,SAAiB,EAAE,KAAa,EAAkC,EAAE;IACrF,MAAM,MAAM,GAAgC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACvE,OAAO,MAAM,KAAK,SAAS;QACzB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACE;gBACE,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,KAAK,GAAG,CAAC;gBACjB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,WAAW,EAAE,MAAM,CAAC,WAAW;aAChC;SACF,CAAA;AACP,CAAC,CAAC,CAAA;AAEJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,IAAY,EAAkC,EAAE,CACvF,IAAI;KACD,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC;KACxB,KAAK,CAAC,IAAI,CAAC;KACX,OAAO,CAAC,CAAC,IAAY,EAAE,KAAa,EAAkC,EAAE,CACvE,gBAAgB,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAClC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** One built client asset and its gzipped size. */
|
|
2
|
+
export interface BundleFile {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly gzipBytes: number;
|
|
5
|
+
}
|
|
6
|
+
/** The outcome of comparing the measured client JS against the budget. */
|
|
7
|
+
export interface BundleReport {
|
|
8
|
+
readonly totalGzipBytes: number;
|
|
9
|
+
readonly budgetBytes: number;
|
|
10
|
+
readonly isWithinBudget: boolean;
|
|
11
|
+
readonly fileCount: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const BUNDLE_BUDGET_BYTES: number;
|
|
14
|
+
/** Sum the gzipped sizes of the built assets and compare the total against the budget. */
|
|
15
|
+
export declare const evaluateBundle: (files: readonly BundleFile[], budgetBytes: number) => BundleReport;
|
|
16
|
+
//# sourceMappingURL=bundle-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-budget.d.ts","sourceRoot":"","sources":["../src/bundle-budget.ts"],"names":[],"mappings":"AAUA,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAkBD,eAAO,MAAM,mBAAmB,EAAE,MAAmC,CAAA;AAErE,0FAA0F;AAC1F,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,UAAU,EAAE,EAAE,aAAa,MAAM,KAAG,YAWlF,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Bundle-size budget policy: the pure logic - the byte ceiling and the sum-vs-budget evaluation - lives
|
|
2
|
+
// here so it is unit-tested; the `bundle` gate in packages/cli/src/checks/integrity.ts walks
|
|
3
|
+
// `.next/static`, gzips each chunk, and reports.
|
|
4
|
+
//
|
|
5
|
+
// Why this is a DETERMINISTIC gate (unlike Lighthouse, which this repo deliberately omits): gzipped
|
|
6
|
+
// byte counts of built JS are a fixed function of the source + pinned dependency and Node versions, so
|
|
7
|
+
// the number changes only when your change changes the bundle - a true signal, never environment flake.
|
|
8
|
+
// It measures the TOTAL client JS (the whole `.next/static` tree), so it catches a catastrophic
|
|
9
|
+
// regression (an accidental heavyweight dependency) rather than a per-route First-Load delta.
|
|
10
|
+
// Total gzipped client-JS ceiling. This is a CONSCIOUS ceiling: raise it deliberately, in a reviewed
|
|
11
|
+
// commit, when a real application needs the room - the way the licence allowlist is widened on purpose,
|
|
12
|
+
// never to silence a regression. A project cannot raise it, only lower it, because `readSettings` takes
|
|
13
|
+
// `Math.min` over the declared value.
|
|
14
|
+
//
|
|
15
|
+
// It was 900 KiB, calibrated against one production build at ~768 KiB with headroom for the Payload
|
|
16
|
+
// admin bundle to grow. A second real Payload application measured 954.6 KiB - and roughly 680 KiB of
|
|
17
|
+
// that was `@payloadcms/ui`, Lexical and the datepicker Payload pulls in, before the project wrote a
|
|
18
|
+
// line of its own. The ceiling was therefore below what the framework this harness exists to govern
|
|
19
|
+
// ships on its own, which made it unreachable rather than demanding: no amount of work on the project's
|
|
20
|
+
// code would have brought it under.
|
|
21
|
+
//
|
|
22
|
+
// The gate is not exercised by `it/`, which builds no application, so a number here is only ever as
|
|
23
|
+
// good as the applications it has been checked against. It has now been checked against two.
|
|
24
|
+
const BYTES_PER_KIB = 1024;
|
|
25
|
+
const BUDGET_KIB = 1100;
|
|
26
|
+
export const BUNDLE_BUDGET_BYTES = BUDGET_KIB * BYTES_PER_KIB;
|
|
27
|
+
/** Sum the gzipped sizes of the built assets and compare the total against the budget. */
|
|
28
|
+
export const evaluateBundle = (files, budgetBytes) => {
|
|
29
|
+
const totalGzipBytes = files.reduce((sum, file) => sum + file.gzipBytes, 0);
|
|
30
|
+
return {
|
|
31
|
+
totalGzipBytes,
|
|
32
|
+
budgetBytes,
|
|
33
|
+
isWithinBudget: totalGzipBytes <= budgetBytes,
|
|
34
|
+
fileCount: files.length,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=bundle-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-budget.js","sourceRoot":"","sources":["../src/bundle-budget.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,6FAA6F;AAC7F,iDAAiD;AACjD,EAAE;AACF,oGAAoG;AACpG,uGAAuG;AACvG,wGAAwG;AACxG,gGAAgG;AAChG,8FAA8F;AAgB9F,qGAAqG;AACrG,wGAAwG;AACxG,wGAAwG;AACxG,sCAAsC;AACtC,EAAE;AACF,oGAAoG;AACpG,sGAAsG;AACtG,qGAAqG;AACrG,oGAAoG;AACpG,wGAAwG;AACxG,oCAAoC;AACpC,EAAE;AACF,oGAAoG;AACpG,6FAA6F;AAC7F,MAAM,aAAa,GAAW,IAAI,CAAA;AAClC,MAAM,UAAU,GAAW,IAAI,CAAA;AAC/B,MAAM,CAAC,MAAM,mBAAmB,GAAW,UAAU,GAAG,aAAa,CAAA;AAErE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAA4B,EAAE,WAAmB,EAAgB,EAAE;IAChG,MAAM,cAAc,GAAW,KAAK,CAAC,MAAM,CACzC,CAAC,GAAW,EAAE,IAAgB,EAAU,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,EAC/D,CAAC,CACF,CAAA;IACD,OAAO;QACL,cAAc;QACd,WAAW;QACX,cAAc,EAAE,cAAc,IAAI,WAAW;QAC7C,SAAS,EAAE,KAAK,CAAC,MAAM;KACxB,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface ParsedMessage {
|
|
2
|
+
readonly header: string;
|
|
3
|
+
readonly body: string;
|
|
4
|
+
}
|
|
5
|
+
export interface DiffStat {
|
|
6
|
+
readonly files: number;
|
|
7
|
+
readonly lines: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Splits a raw commit message into header and body, dropping git comment lines and any verbose-diff
|
|
11
|
+
* section below the scissors marker.
|
|
12
|
+
* @param raw the full commit-message file contents.
|
|
13
|
+
* @returns the first non-blank line as `header` and the remaining prose as `body` (both may be empty).
|
|
14
|
+
*/
|
|
15
|
+
export declare const parseMessage: (raw: string) => ParsedMessage;
|
|
16
|
+
/**
|
|
17
|
+
* Validates a parsed commit message, returning a human-readable problem per rule violation. Every
|
|
18
|
+
* commit is held to every rule: there is no exemption list, so a git-generated merge, revert, or
|
|
19
|
+
* autosquash subject must be rewritten by hand to conform.
|
|
20
|
+
* @param message the header/body pair from {@link parseMessage}.
|
|
21
|
+
* @param isBodyRequired whether the change is non-trivial and therefore must carry an explanatory body.
|
|
22
|
+
* @returns an empty array when the message passes.
|
|
23
|
+
*/
|
|
24
|
+
export declare const validateMessage: (message: ParsedMessage, isBodyRequired: boolean) => readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* Parses `git --numstat` output into a file count and a total changed-line count.
|
|
27
|
+
* @param numstat the raw stdout of a `--numstat` diff (tab-separated added/deleted/path rows).
|
|
28
|
+
* @returns the number of changed files and the sum of added plus deleted lines (binary rows count 0).
|
|
29
|
+
*/
|
|
30
|
+
export declare const parseNumstat: (numstat: string) => DiffStat;
|
|
31
|
+
/**
|
|
32
|
+
* Decides whether a diff is large enough to require an explanatory commit body.
|
|
33
|
+
* @param stat the file and line counts from {@link parseNumstat}.
|
|
34
|
+
* @returns true when the change touches more than 2 files or more than 50 lines.
|
|
35
|
+
*/
|
|
36
|
+
export declare const isNonTrivial: (stat: DiffStat) => boolean;
|
|
37
|
+
//# sourceMappingURL=commit-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-message.d.ts","sourceRoot":"","sources":["../src/commit-message.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,aAmB1C,CAAA;AAgED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,EACtB,gBAAgB,OAAO,KACtB,SAAS,MAAM,EAMjB,CAAA;AAOD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,QAS9C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,QAAQ,KAAG,OACoB,CAAA"}
|