	Revision history for CSS::Minifier

0.0.13  2026-06-14T05:52:39+03:00
    - Fix: $UNSAFE-RE now guards all CSS Values Level 4 math functions
      (abs, sign, round, mod, rem, pow, sqrt, hypot, sin, cos, tan,
      atan, atan2, asin, acos, exp, log) — prevents incorrect
      zero-unit stripping and color normalization inside these
      functions
    - Fix: !consolidate-shorthands dropped longhands with
      non-lowercase names; !write-at-rule omitted ; before
      trailing-declarations; unclosed /*! at EOF leaked into output;
      CSS_MINIFIER_CACHE_MAX=0 caused div by zero; gap-text heuristic
      now rejects tokens containing (
    - Fix: !url-quote strips outer quotes and escapes inner matching
      quotes; $td.normalized = Nil instead of Str (Merging)
    - Fix: LRU hit-path promotion now fires during fill;
      declarations-to-key no longer caches .normalized as side effect;
      !url-quote-value avoids re-combing the same string via
      skip-quoted(@chars, ...)
    - Perf: hot loops use pre-comb'd @chars[\$j]; parse-body uses
      \$body.substr(\$i, \$end-\$i) instead of slice-then-join;
      find-braceless-end skips past */ closing slash directly
    - Quality: consistent indentation in Minifier.rakumod;
      find-braceless-end uses skip-comment-chars; ring-buffer
      arithmetic documented; dead default guards annotated

0.0.12  2026-06-14T02:58:17+03:00
    - Fix: CLI — -l 1 (separate arg) now works; root cause was Raku
      regex [12] being a group matching "12", not a character class;
      also refactored given/when → if/elsif to avoid implicit-break
      interaction with next
    - Fix: strip-zero-units now safe inside quoted strings; handles
      .0px/.0em (leading-digit-less decimals) and respects url() with
      arbitrary paren nesting; $1. alpha in rgb()/hsl() matches
      trailing dot with no zeros
    - Fix: backslash-escaped braces no longer miscounted as block
      delimiters; unclosed /*! at EOF preserved in output; } at depth
      0 before any { no longer crashes via Nil brace in substr
    - Security: $SENTINEL broadened to \x[E000]\x[E001] to virtually
      eliminate PUA collision risk
    - Quality: !split-gap-buffer skips tokens containing quotes;
      default {die} guards on Statement dispatch; license extraction
      returns Pair (no $doc mutation); Declaration.normalized default
      Str → Nil
    - Perf: O(1) position-tracked LRU in comb-cached (every hit
      promotes); .comb.List moved outside lock; O(n²) → O(n) in
      parse-body/ strip-braceless-comments; $h %= 360; pass @sel-chars
      to skip-quoted

0.0.11  2026-06-13T05:08:48+03:00
    - Fix: parser — declarations between nested at-rule blocks not
      dropped; gap text without `;` correctly split; braceless at-rule
      comment before `;` handled; !find-block-range skips `/* */`
      comments; case-insensitive property sort in declarations-to-key;
      EOS zero-unit `<!after <:L>>` guard
    - Fix: normalization — digit-only hex shortened; :color-names
      font-weight/ zero-units preserved; redundant !apply-font-weight
      removed; decimal rgb rounding; !color-masks :short flag
      respected; symmetric custom-prop merge barrier; indent 4-spaces
      in !merge-decls
    - Fix: dedup/merging — trailing-declarations barriers for both
      Dedup and Merging; redundant key extraction; case-insensitive
      shorthand/merge lookup; custom property case preservation
    - Fix: CLI — -o without argument gives clear "Missing file path"
      -error; o without value; !extra-plugins at level 1
    - Perf: O(1) ring-buffer comb-cache eviction; restore comb-cached
      for long selectors; cache @body-chars via comb-cached in
      !parse-at-rule; clear-comb-cache per minify(); LRU
      direct-return; remove 148-entry named-color guard; reuse
      !find-block-range for trailing-decl scan
    - Quality: %DISPATCH anon-sub → direct given dispatch; dead
      :track-last-close & $close & @.braceless removed;
      has-top-level-comma → split-top-level .elems check;
      extract-braceless-at helper; guard-color inlined; C-style loop →
      .reverse; .elems > 0 checks; $no-color-ops → $full-normalize;
      --> Str() for Nil returns; COMB-CACHE-SMALL env override; bare
      `|` resets $last-comb; !extract-braceless simplified;
      combine-adjacent-stmts; strip-braceless-comments;
      with-url-quarantine/normalize-value subs; braceless as AtRule
      objects
    - Doc: POD for clear-comb-cache; CSS native nesting unsupported
      note
    - Test: baseline 100%; 9+ new assertions

0.0.10  2026-06-12T06:05:38+03:00
    - Fix: color/value normalization — balanced-paren URL quarantine;
      hsl→hex; hsl/hsla rejects 1% alpha; rgb-values-to-hex bare 100
      alpha & leading-dot numbers; font-weight exact match; case-fold
      dedup (Normalizer, Writer); UNSAFE-RE guard; embedded-unit
      guard; 8-digit hex anchor; rgb-values clamp; remove bogus
      post\-rule\; dead-code cleanup
    - Fix: parser — quote-aware comment stripping for /* inside
      url("..."); @layer; semicolon-terminated at-rules (braceless
      flag); depth-underflow guards; ] combinator $last-comb reset;
      stray * before braceless @-rule; match-variable scoping
    - Fix: dedup/merging — selector sort, trailing-declarations
      at-rule key, combinator dedup reset on ), !important cascade,
      empty-ruleset barrier, Dedup comment broadened beyond @font-face
    - Perf: regex→junction (~20% faster parse); O(1) comb-cache
      eviction; substr window; named-color pre-check (~10× pipeline);
      decl-key cache
    - Code quality: dead-code removal, %NAMED/HEX-TO-NAME expansion,
      %COLOR-PROPS shared, CLI --level validation; misc edge-cases;
      review-5 subtest
    - Fix: case-insensitive color/font-weight guards (Writer,
      Document); case-insensitive !merge-decls lookup; custom property
      case preserved in Normalizer dedup and Writer output
    - Test: 5 new test files (at-rule-nesting, unicode, selectors,
      supports); 111+ new assertions across 7 files

0.0.9  2026-06-06T05:43:28+03:00
    - Refactor: consolidate color/unit helpers in Util, method cleanup
      (!same-selectors→eqv, !make-declaration, inline !decl-key, etc.)
    - Fix: parser edge cases (has-brace off-by-one, bare-/ double-inc,
      etc.)
    - Fix: normalization (hex folding, !normalize-quotes \" escape,
      rgb float %, empty background guard, non-color props removed
      from %COLOR-PROPS)
    - Fix: dedup/merge (return instead of mutate, bold/normal word
      boundaries, selector identity over .fc, revert-layer, AtRule
      barrier check)
    - Fix: rgb() rounding (50%→#80), selector case-sensitivity, CLI
      --level
    - Perf: fast-path guards, grep+map combine, comb-cache gradual
      eviction, skip color-masks when color-names enabled
    - Code quality: custom-property skip in declarations-to-key,
      CSS_MINIFIER_CACHE_MAX env var, !assemble-background-multi
      manual max-loop
    - Test: delete 01-basic (merged into golden), qqx→run, new
      coverage

0.0.8  2026-06-05T07:46:49+03:00
    - ~15× perf: comb-cache with binding, skip-quoted copy
      elimination, .chars caching in hot loops
    - Color masking fixes: url() quarantine, rgb→named, hex→name
      word-boundary, Level 4 space-sep rgb, rgba 1.0, %COLOR-PROPS
      fast-path
    - Parser fixes: OOB in !extract-braceless, find-semicolon $i++,
      bare / fallthrough, depth-tracked @import
    - Writer fixes: .0 edge case, null-byte sentinel, url-quote
      backtrack, UNITS constant, url-quote-value helper
    - Plugin fixes: Dedup added to pipeline, extra-plugins die guard,
      at-rule-decl-key case sensitivity, dead Values plugin removed
    - Dedup/Merging: normalize colors (named→hex, bold→700) and
      rgb→hex in declarations-to-key, Unicode .fc in selector dedup
    - Refactor: declarations-to-key helper, %DISPATCH table, helper
      extraction, dead return cleanup
    - Other: CLI --level, @@name, modern CSS units
      (cap,ic,lh,rlh,vb,vi,cq*), comb-cache 500-cap + Lock thread
      safety

0.0.7  2026-06-04T03:35:20+03:00
    - AST rewrite: own Parser/Document/Normalizer replaces
      CSS::Stylesheet/ CSS::Writer/CSS::Properties — zero deps
      (META6.json []), drop :$preserve-unknown
    - Add !dedup-declarations within a ruleset
    - Add !consolidate-shorthands (margin, padding, border, outline,
      list-style, background, font); skip var()/calc() etc.; fix
      duplicate-declaration, ordering, mixed-!important bugs
    - Fix !write-at-rule space check, !extract-braceless quote
      walkers, Writer quote norm/hex uppercasing
    - Add Dedup !important consistency, background/font consolidation
      (multi-layer with cycling, size/origin/clip; font with stretch/
      line-height), %CUSTOM dispatch table, assembly helpers, comma
      helpers in Util, $CSS-WIDE-RE guard

0.0.6  2026-06-03T21:12:55+03:00
    - Fix re-minification growth (final): colon in media-feature regex
      `/\(.*:.*\)/` was being interpreted as a Raku regex adverb (`:`)
      instead of a literal colon, so the denormalize-prelude fallback
      in !find-at-rule-block was never triggered.  This caused
      !prop-exists to always return False for any @media qualifier
      whose prelude had been space-normalized by !walk-decls, because
      the exact prelude search (with space after `:`) never matched
      the pipeline output (without space after `:`).  Consequence:
      every property inside @media was re-injected on every pass,
      causing unbounded growth.
    - Add normalize-prelude / denormalize-prelude subroutines to
      bridge the two prelude formats
    - Normalize preludes in !walk-decls so qualifier keys are
      consistent
    - Fix !find-at-rule-block to try both normalized and denormalized
      forms during block search
    - Fix !prop-exists to scan ALL at-rule blocks with the same
      prelude (not just the first) for the inner selector
    - Fix !merge-qualified to find the at-rule block that actually
      contains the target inner selector; add idempotency guard to
      skip re-injection when the exact ruleset already exists in the
      block
    - Fix normalize/denormalize-prelude nested-parens handling
      (e.g. calc(), min(), max()) — use a token with &val to match
      balanced inner parens instead of a bare <-[):]>+ capture

0.0.5  2026-06-03T06:24:07+03:00
    - Fix !normalize-block trailing semicolon: always omit (rebuilds
      from scratch), making output consistent with pipeline blocks
    - Fix !find-qual-end depth counting: O(n) single-pass with
      quote/comment skipping instead of O(n²) rescan from 0
    - Fix !normalize-selector quote walking: use skip-quoted for
      backslash-escape handling
    - Fix split-selectors quote walking: use skip-quoted for
      backslash-escape handling (same bug, Util.rakumod)
    - Update Merging.rakumod POD: propertyless rules are skipped
      (not barriers)
    - Update README Level 2: add propertyless-rules note
    - Add test coverage: escaped-quote attribute selector, content
      brace depth guard, @keyframes margin:0px, no-trailing-newline
    - Fix !normalize-all-decls inline quote walkers: use skip-quoted
      (fixes double-backslash \" edge case)
    - Fix split-xpath quote walking: use skip-quoted for consistency
      (Util.rakumod)

0.0.4  2026-06-03T04:48:31+03:00
    - Fix order stability: insert re-injected rulesets at their
      natural @order position (after predecessor) instead of appending
      at end, so first-pass and second-pass selector order is
      identical
    - Add rgb()/rgba() → hex shortening to !minify-value
      (post-processing pass), closing the gap where rgb() values
      inside @keyframes and other pipeline-bypassed blocks stayed
      unshortened
    - Remove stale Merging-plugin !important warning from README —
      !important is part of the Str(:optimize) grouping key, so mixed
      groups never form
    - Fix re-minification growth (remaining): normalize combinator
      spacing (+ / > / ~) in !normalize-selector so selectors like
      .foo+.bar (no space before +) match the pipeline output .foo +
      .bar (with space).  Always call !normalize-selector (not just
      for comma-containing selectors) to catch this case. Prevents
      runaway re-injection on subsequent passes for any selector
      containing tight combinators.

0.0.3  2026-06-03T03:39:52+03:00
    - Fix braceless at-rules (@import/@charset/@namespace) silently
      dropped
    - Fix @view-transitions/declaration-only at-rules dropped;
      @font-face descriptors dropped by !walk-decls
    - Fix !merge-props !important cascade; :has()/:is()/:where() dupe
      rulesets
    - Fix bogus @media not all detection, Nth-block targeting in
      !find-at-rule-block/!prop-exists, quote normalisation, regex
      injection
    - Rewrite tests: exact golden output, add 03-edge-cases/04-golden
    - Fix merge-qualified/remove-selector/walk-decls/dedup-selector
      bugs
    - Fix xpath crash on ::pseudo-elements (monkey-patch) and
      idempotency
    - Fix non-adjacent merging with propertyless rules, cascade-order
      during same-decl merge, @keyframes selector order (@order array)
    - Rewrite !walk-decls with parse-body character-walker (handles
      strings, parens, comments)
    - Rewrite /*! */ license extraction with character-walking state
      machine
    - Add !minify-value (color, zero-unit, font-weight, quote
      normalization) to re-injection path; add !normalize-all-decls
      post-processing pass
    - Fix hex shortening regex, zero-unit negative-lookbehind,
      parse-body double-increment, quote-aware walkers
    - Fix re-minification growth: normalize selectors/preludes to
      match pipeline output format

0.0.2  2026-05-21T16:27:43+03:00
    - Fix `1em` → `em` regression from CSS::Writer (override write-num
      for `em`/`ex` in Writer.rakumod)
    - Remove %KNOWN-PROPS filter in !walk-decls so all known
      properties are pre-scanned (catches CSS3 values like `position:
      sticky`, `overflow: clip` that the grammar drops)
    - Fix falsy-zero bug in !prop-exists (Raku `0` is falsy, causing
      all rulesets at position 0 to always re-inject)
    - Add %SHORTHAND map in !prop-exists to avoid re-injecting
      longhands consolidated to shorthands by CSS::Properties
      (e.g. `background-color` → `background`)
    - Fix same falsy-zero bug in at-rule path of !prop-exists

0.0.1  2026-05-21T01:30:31+03:00
    - Initial version
