feat(13-01): add the prohibited rule and dated-download and notification masks

- lagoon.ValidateRequest supports Laravel 9 prohibited (!required, not
  implicit); with no catalog line its message is validation.prohibited
- tide compares Content-Disposition with real calendar dates masked on both
  sides; a different name, an invalid date or a one-sided date still diffs
- tide.NormalizePublications masks a Carbon +00:00 $.data.payload.created_at
  and an uncaptured positive integer $.data.payload.id as {{id}}
- the album-date test's outside-album sibling moves off payload.created_at,
  which now has its own mask
- READMEs and docs describe the rule and both masks
This commit is contained in:
Jakub Zych
2026-10-03 06:32:46 +02:00
parent 55a4092019
commit 2b94dfd2d2
10 changed files with 243 additions and 9 deletions

View File

@@ -275,7 +275,27 @@ func (n *normalizer) lookup(v string) (string, bool) {
}
}
// positiveIntRe is a JSON number literal holding a positive integer.
var positiveIntRe = regexp.MustCompile(`^[1-9][0-9]*$`)
func (n *normalizer) walk(path, key string, v *onode) *onode {
// A notification publication carries the new row's id and created_at
// under $.data.payload. A Carbon +00:00 created_at is masked like an album
// date; a positive integer id keeps its captured variable's placeholder
// and is {{id}} when no captured variable names it. Any other shape at
// these paths stays visible.
switch {
case path == "$.data.payload.created_at" && v.kind == kindString && carbonOffsetRe.MatchString(v.str):
return &onode{kind: kindString, str: "{{datetime}}"}
case path == "$.data.payload.id" && v.kind == kindNumber && positiveIntRe.MatchString(v.str):
if name, ok := n.lookup(v.str); ok {
return &onode{kind: kindPlaceholder, str: name}
}
if len(n.ids[v.str]) == 0 {
return &onode{kind: kindPlaceholder, str: "id"}
}
return v
}
switch path {
case "$.data.timestamp", "$.data.payload.timestamp":
if v.kind == kindString && isoOffsetRe.MatchString(v.str) {