Curated, product-agnostic snapshot of the post-story-04 tree: demo content deleted, auth-only reference feature, web-next shell, all gates green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor library traces, and product naming are curated out; generic template repairs (coverage provider devDeps, root test:coverage script, live lint fixes, root-only release-please) are kept. See TEMPLATE.md for provenance, curation list, and usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
58 lines
1.5 KiB
JavaScript
58 lines
1.5 KiB
JavaScript
const REQUIRED_FIELDS = ["category", "purpose", "exportable", "restrictable"];
|
|
|
|
/** @type {import("eslint").Rule.RuleModule} */
|
|
export default {
|
|
meta: {
|
|
type: "problem",
|
|
docs: {
|
|
description:
|
|
"custom.pii blocks in Payload config files must declare all required sub-fields: category, purpose, exportable, restrictable.",
|
|
},
|
|
schema: [],
|
|
messages: {
|
|
missingField:
|
|
"custom.pii block is missing required field '{{field}}'. Incomplete PII declarations can produce incorrect audit reports.",
|
|
},
|
|
},
|
|
create(context) {
|
|
return {
|
|
Property(node) {
|
|
if (
|
|
node.key.type !== "Identifier" ||
|
|
node.key.name !== "custom" ||
|
|
node.value.type !== "ObjectExpression"
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const piiProp = node.value.properties.find(
|
|
(p) =>
|
|
p.type === "Property" &&
|
|
p.key.type === "Identifier" &&
|
|
p.key.name === "pii",
|
|
);
|
|
|
|
if (!piiProp || piiProp.value.type !== "ObjectExpression") {
|
|
return;
|
|
}
|
|
|
|
const presentFields = new Set(
|
|
piiProp.value.properties
|
|
.filter((p) => p.type === "Property" && p.key.type === "Identifier")
|
|
.map((p) => p.key.name),
|
|
);
|
|
|
|
for (const field of REQUIRED_FIELDS) {
|
|
if (!presentFields.has(field)) {
|
|
context.report({
|
|
node: piiProp,
|
|
messageId: "missingField",
|
|
data: { field },
|
|
});
|
|
}
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|