@octanejs/seo 0.0.36 → 0.0.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/seo",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -30,11 +30,11 @@
30
30
  ".": "./src/index.ts"
31
31
  },
32
32
  "peerDependencies": {
33
- "octane": "0.1.51"
33
+ "octane": "^0.1.51 || ^0.2.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "vitest": "^4.1.10",
37
- "octane": "0.1.51"
37
+ "octane": "0.2.4"
38
38
  },
39
39
  "scripts": {
40
40
  "test": "vitest run"
@@ -30,20 +30,28 @@ export interface SeoDescriptor {
30
30
  * Settings that belong to the app rather than to one declaration. They are
31
31
  * registered like any other metadata and merged last-wins, so setting them once
32
32
  * near the root applies them to every page's title and URLs.
33
+ *
34
+ * Every field admits `undefined` as well as being optional. A caller assembles
35
+ * this from props that are themselves optional, and the merge in `applyConfig`
36
+ * reads each field with an `!== undefined` test, so an absent key and a present
37
+ * `undefined` have always meant the same thing here. Spelling that out is what
38
+ * lets the object be built in one expression under `exactOptionalPropertyTypes`,
39
+ * which matters because this package publishes source and the consumer's
40
+ * compiler options are the ones that compile it.
33
41
  */
34
42
  export interface SeoConfig {
35
43
  /** Origin used to absolute-ise canonical, og:url, and image URLs. */
36
- site?: string;
44
+ site?: string | undefined;
37
45
  /** `%s` is replaced by the page title, e.g. `'%s · Acme'`. */
38
- titleTemplate?: string;
46
+ titleTemplate?: string | undefined;
39
47
  /**
40
48
  * Whether an `openGraph`/`twitter` block was declared. Opting in is about
41
49
  * having asked for the family, not about which tags that produced:
42
50
  * `openGraph: { publishedTime }` emits only `article:published_time`, which no
43
51
  * `og:` prefix scan would recognise.
44
52
  */
45
- declaredOpenGraph?: boolean;
46
- declaredTwitter?: boolean;
53
+ declaredOpenGraph?: boolean | undefined;
54
+ declaredTwitter?: boolean | undefined;
47
55
  }
48
56
 
49
57
  /**
package/src/expand.ts CHANGED
@@ -148,7 +148,7 @@ export function expandSeo(input: SeoInput): SeoDescriptor[] {
148
148
  // merge, so one place decides which origin applies.
149
149
  const images = normalizeImages(og.images);
150
150
  for (let i = 0; i < images.length; i++) {
151
- const image = images[i];
151
+ const image = images[i]!;
152
152
  const url = image.url;
153
153
  // Repeated og:image tags are legitimate, so each gets its own identity.
154
154
  out.push({
package/src/registry.ts CHANGED
@@ -38,8 +38,8 @@ export interface SeoRegistry {
38
38
  function sameDescriptors(a: readonly SeoDescriptor[], b: readonly SeoDescriptor[]): boolean {
39
39
  if (a.length !== b.length) return false;
40
40
  for (let i = 0; i < a.length; i++) {
41
- const x = a[i];
42
- const y = b[i];
41
+ const x = a[i]!;
42
+ const y = b[i]!;
43
43
  if (x.key !== y.key || x.tag !== y.tag || x.text !== y.text) return false;
44
44
  const xa = x.attrs;
45
45
  const ya = y.attrs;
@@ -16,11 +16,25 @@
16
16
  */
17
17
  import { useEffect } from 'octane';
18
18
 
19
+ // This package publishes source, so a browser application compiles the file
20
+ // below with its own tsconfig and runs it in a host that has neither
21
+ // `@types/node` nor a `process` global. The reference is therefore declared
22
+ // here and guarded at runtime; without the guard every `<Head>` render throws a
23
+ // ReferenceError in any host that does not define one.
24
+ //
25
+ // The read stays spelled out as `process.env.NODE_ENV` because that is the
26
+ // expression bundlers substitute with a literal. Where the identifier also
27
+ // survives, Node and SSR, the production branch is exact. Where only the value
28
+ // is substituted the guard reads false and the diagnostic behaves as it does in
29
+ // development: one counter and one effect per owning `<Head>`, which is the
30
+ // price of not throwing.
31
+ declare const process: { readonly env: { readonly NODE_ENV?: string } } | undefined;
32
+
19
33
  let liveOwners = 0;
20
34
  let reported = false;
21
35
 
22
36
  export function useStrayOwnerDiagnostic(owns: boolean): void {
23
- if (process.env.NODE_ENV === 'production') return;
37
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') return;
24
38
  useEffect(() => {
25
39
  if (!owns) return;
26
40
  liveOwners++;