@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 +11 -0
- package/package.json +2 -2
- package/src/images.ts +9 -1
- package/src/index.ts +0 -1
- package/src/meta.ts +5 -2
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": "
|
|
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": "
|
|
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
|
-
|
|
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
package/src/meta.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { absoluteUrl } from './xml';
|
|
6
6
|
|
|
7
|
-
/**
|
|
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 {
|