@ultimat3/seo 8.0.0 → 10.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
@@ -82,6 +82,11 @@ Tier 1. May import `@ultimat3/core`, `@ultimat3/schema`, `@ultimat3/i18n`. Nothi
82
82
  whether that string is a path, a storage key or a URL is the app's fact, not seo's — never add
83
83
  a filesystem fallback. Pixels come from `@ultimat3/core`'s pipeline; seo owns no second scaler,
84
84
  and the driver reports the size it probed off the output, never the size that was requested.
85
+ - **A table keyed on a caller string is read with `Object.hasOwn`, never a bare index.**
86
+ `MIME_TYPES[format]` in `images.ts` walked the prototype, so `formats: ['constructor']` rendered
87
+ `<source type="function Object() { [native code] }">` — into the one attribute a browser reads to
88
+ decide whether to fetch the candidate at all. `mimeTypeFor` is the own-key read; the fallback is
89
+ still `image/<format>`.
85
90
  - **One spelling of the transform query keys.** `IMAGE_QUERY_KEYS` in `images.ts` is the only
86
91
  place `w`/`f`/`q` are spelled; `defaultUrlFor` writes them and `parseImageQuery` is the only
87
92
  reader — never hand-roll either half against a literal. A present-but-unusable `w` or `q`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/seo",
3
- "version": "8.0.0",
3
+ "version": "10.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": "8.0.0"
34
+ "@ultimat3/core": "10.0.0"
35
35
  }
36
36
  }
package/src/errors.ts CHANGED
@@ -55,7 +55,6 @@ export class SeoError extends UltimateError {
55
55
  code: init.code,
56
56
  cause: init.cause,
57
57
  fix: init.fix,
58
- docs: `https://ultimate.dev/errors/${init.code}`,
59
58
  meta: init.meta,
60
59
  });
61
60
  }
package/src/images.ts CHANGED
@@ -186,6 +186,11 @@ export function srcsetFor(
186
186
  return widths.map((width) => `${urlFor(input.src, width, format)} ${width}w`).join(', ');
187
187
  }
188
188
 
189
+ /** The declared MIME type, else the conventional `image/<format>`. Own keys only. */
190
+ function mimeTypeFor(format: string): string {
191
+ return Object.hasOwn(MIME_TYPES, format) ? (MIME_TYPES[format] as string) : `image/${format}`;
192
+ }
193
+
189
194
  export function responsiveImage(
190
195
  input: ImageInput,
191
196
  options: ResponsiveImageOptions = {},
@@ -196,7 +201,10 @@ export function responsiveImage(
196
201
  const formats = options.formats ?? FORMAT_ORDER;
197
202
 
198
203
  const sources: ImageSourceSet[] = formats.map((format) => ({
199
- type: MIME_TYPES[format] ?? `image/${format}`,
204
+ // `Object.hasOwn`, never a bare index: `MIME_TYPES` is a `{}`-prototyped literal, so an
205
+ // unlisted format spelled `constructor` or `toString` rendered a function into the one
206
+ // attribute a browser reads to decide whether to fetch the candidate at all.
207
+ type: mimeTypeFor(format),
200
208
  srcset: srcsetFor(input, widths, format, urlFor),
201
209
  sizes,
202
210
  }));