@ultimat3/seo 2.0.0 → 3.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/CLAUDE.md CHANGED
@@ -28,6 +28,17 @@ Tier 1. May import `@ultimat3/core`, `@ultimat3/schema`, `@ultimat3/i18n`. Nothi
28
28
  over the route manifest and the build's own stats, throwing `@ultimat3/render`'s
29
29
  `X_BUDGET_EXCEEDED`. seo is tier 1 and cannot see a build's bytes, so it was never the package
30
30
  that could answer. `errors.test.ts` pins the code set, so re-adding one is a failing test.
31
+ - **A length bound with no enforcer does not ship.** `DESCRIPTION_MIN_LENGTH` (50) sat in
32
+ `meta.ts` under the comment "validate.ts enforces it", was re-exported from `index.ts`, and no
33
+ validator anywhere read it — a 10-character description passed the gate the constant claimed to
34
+ fail. Deleted `As of 2026-08`, comment included; `validateMeta` enforces maxima only. Adding a
35
+ minimum back means adding the check AND a new `X_SEO_*` code in the same change.
36
+ `meta.test.ts` pins the exported `*_LENGTH` set, so a bound with no enforcer is a failing test.
37
+ - **The `<img src>` fallback is the LARGEST usable width, chosen with `Math.max`.**
38
+ `usableWidths` preserves the CALLER's order, so `widths[widths.length - 1]` was the largest only
39
+ because `DEFAULT_WIDTHS` happens to ascend — `widths: [1200, 640]` handed every browser without
40
+ `srcset` support the 640 variant of a 1200-wide image. Never re-derive it from position, and
41
+ never sort inside `usableWidths`: the `srcset` order is the caller's to choose.
31
42
  - **Errors name the file, not the URL.** `RouteRecord.file` is in every cause and every fix; an agent must be able to open the source without guessing.
32
43
  - **Fail closed, and core reads the key.** `isIndexable()` is `environment === 'production'` and
33
44
  nothing else — `staging`, a laptop, a typo and an unset variable all disallow. `ULTIMATE_ENV` has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/seo",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Enforced SEO: typed meta, JSON-LD, sitemap, robots, feeds, responsive images, perf budgets",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,6 +31,6 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "2.0.0"
34
+ "@ultimat3/core": "3.0.0"
35
35
  }
36
36
  }
package/src/images.ts CHANGED
@@ -176,6 +176,11 @@ export function usableWidths(intrinsic: number, widths: readonly number[]): read
176
176
  return usable.includes(intrinsic) ? usable : [...usable, intrinsic];
177
177
  }
178
178
 
179
+ /** The widest candidate, or `undefined` for an empty list — never `Math.max()`'s `-Infinity`. */
180
+ function largestOf(widths: readonly number[]): number | undefined {
181
+ return widths.length === 0 ? undefined : Math.max(...widths);
182
+ }
183
+
179
184
  export function srcsetFor(
180
185
  input: ImageInput,
181
186
  widths: readonly number[],
@@ -212,7 +217,10 @@ export function responsiveImage(
212
217
  return {
213
218
  sources,
214
219
  img: {
215
- src: urlFor(input.src, widths[widths.length - 1] ?? input.width, undefined),
220
+ // `Math.max`, not the last element: `usableWidths` preserves the CALLER's order, so the
221
+ // tail was the largest only because `DEFAULT_WIDTHS` happens to ascend. `widths: [1200, 640]`
222
+ // handed every no-`srcset` browser the 640 variant of a 1200-wide image.
223
+ src: urlFor(input.src, largestOf(widths) ?? input.width, undefined),
216
224
  srcset: srcsetFor(input, widths, undefined, urlFor),
217
225
  sizes,
218
226
  alt: input.alt,
package/src/index.ts CHANGED
@@ -84,7 +84,6 @@ export type {
84
84
  export {
85
85
  applyTitleTemplate,
86
86
  DESCRIPTION_MAX_LENGTH,
87
- DESCRIPTION_MIN_LENGTH,
88
87
  hreflangSet,
89
88
  renderMeta,
90
89
  robotsContent,
package/src/meta.ts CHANGED
@@ -4,9 +4,12 @@
4
4
 
5
5
  import { absoluteUrl } from './xml';
6
6
 
7
- /** Search results truncate past this; validate.ts enforces it. */
7
+ /**
8
+ * Search results truncate past these; `validate.ts` enforces both, and only these two exist for
9
+ * that reason — a `DESCRIPTION_MIN_LENGTH` shipped here with no validator reading it, so the
10
+ * comment promised a gate that never ran. A bound with no enforcer does not ship.
11
+ */
8
12
  export const TITLE_MAX_LENGTH = 60;
9
- export const DESCRIPTION_MIN_LENGTH = 50;
10
13
  export const DESCRIPTION_MAX_LENGTH = 160;
11
14
 
12
15
  export interface RobotsDirectives {