@websolutespa/bom-next 4.0.0-next.2 → 4.0.0-next.4

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +61 -11
  3. package/dist/client/client.d.ts +2 -2
  4. package/dist/client/extra.d.ts +3 -3
  5. package/dist/client/extra.d.ts.map +1 -1
  6. package/dist/client/label.d.ts +3 -3
  7. package/dist/client/layout.d.ts +3 -3
  8. package/dist/client/layout.d.ts.map +1 -1
  9. package/dist/client/metadata.d.ts +10 -0
  10. package/dist/client/metadata.d.ts.map +1 -1
  11. package/dist/client/metadata.js +27 -46
  12. package/dist/client/metadata.js.map +1 -1
  13. package/dist/client/page.d.ts +4 -3
  14. package/dist/client/page.d.ts.map +1 -1
  15. package/dist/client/useApi.d.ts +7 -7
  16. package/dist/client/useCollectionApi.d.ts +6 -6
  17. package/dist/client/useCollectionApi.d.ts.map +1 -1
  18. package/dist/client/usePage.d.ts +1 -0
  19. package/dist/client/user.d.ts +15 -8
  20. package/dist/client/user.d.ts.map +1 -1
  21. package/dist/client/user.js +37 -14
  22. package/dist/client/user.js.map +1 -1
  23. package/dist/exports/client.d.ts +2 -2
  24. package/dist/exports/client.js +2 -2
  25. package/dist/exports/proxy.d.ts +3 -3
  26. package/dist/exports/proxy.js +3 -3
  27. package/dist/exports/server.d.ts +6 -4
  28. package/dist/exports/server.js +6 -4
  29. package/dist/lib/debug.js +14 -0
  30. package/dist/lib/debug.js.map +1 -0
  31. package/dist/lib/http.d.ts.map +1 -1
  32. package/dist/lib/http.js +2 -1
  33. package/dist/lib/http.js.map +1 -1
  34. package/dist/lib/meta-data.d.ts.map +1 -1
  35. package/dist/lib/page-metadata.d.ts +57 -0
  36. package/dist/lib/page-metadata.d.ts.map +1 -0
  37. package/dist/lib/page-metadata.js +205 -0
  38. package/dist/lib/page-metadata.js.map +1 -0
  39. package/dist/lib/ttl-cache.js +59 -0
  40. package/dist/lib/ttl-cache.js.map +1 -0
  41. package/dist/proxy/protected-route.d.ts.map +1 -1
  42. package/dist/proxy/protected-route.js +3 -2
  43. package/dist/proxy/protected-route.js.map +1 -1
  44. package/dist/proxy/route-auto-detection.d.ts +6 -1
  45. package/dist/proxy/route-auto-detection.d.ts.map +1 -1
  46. package/dist/proxy/route-auto-detection.js +68 -25
  47. package/dist/proxy/route-auto-detection.js.map +1 -1
  48. package/dist/proxy/route-proxy.d.ts +4 -1
  49. package/dist/proxy/route-proxy.d.ts.map +1 -1
  50. package/dist/proxy/route-proxy.js +96 -14
  51. package/dist/proxy/route-proxy.js.map +1 -1
  52. package/dist/server/handlers/registry.handler.d.ts.map +1 -1
  53. package/dist/server/handlers/registry.handler.js +77 -11
  54. package/dist/server/handlers/registry.handler.js.map +1 -1
  55. package/dist/server/handlers/route.handler.d.ts +12 -4
  56. package/dist/server/handlers/route.handler.d.ts.map +1 -1
  57. package/dist/server/handlers/route.handler.js +54 -39
  58. package/dist/server/handlers/route.handler.js.map +1 -1
  59. package/dist/server/helpers.d.ts +1 -4
  60. package/dist/server/helpers.d.ts.map +1 -1
  61. package/dist/server/json-ld.d.ts +33 -0
  62. package/dist/server/json-ld.d.ts.map +1 -0
  63. package/dist/server/json-ld.js +50 -0
  64. package/dist/server/json-ld.js.map +1 -0
  65. package/dist/server/layout.js +2 -1
  66. package/dist/server/layout.js.map +1 -1
  67. package/dist/server/lazy.js +8 -11
  68. package/dist/server/lazy.js.map +1 -1
  69. package/dist/server/metadata.d.ts +29 -10
  70. package/dist/server/metadata.d.ts.map +1 -1
  71. package/dist/server/metadata.js +36 -12
  72. package/dist/server/metadata.js.map +1 -1
  73. package/dist/server/mixer.d.ts +16 -7
  74. package/dist/server/mixer.d.ts.map +1 -1
  75. package/dist/server/mixer.js +102 -122
  76. package/dist/server/mixer.js.map +1 -1
  77. package/dist/types.d.ts +6 -3
  78. package/dist/types.d.ts.map +1 -1
  79. package/package.json +9 -6
  80. package/src/client/metadata.tsx +22 -57
  81. package/src/client/user.test.tsx +44 -0
  82. package/src/client/user.tsx +47 -14
  83. package/src/exports/server.ts +1 -0
  84. package/src/lib/debug.ts +11 -0
  85. package/src/lib/http.ts +2 -1
  86. package/src/lib/page-metadata.test.ts +126 -0
  87. package/src/lib/page-metadata.ts +231 -0
  88. package/src/lib/ttl-cache.ts +72 -0
  89. package/src/proxy/protected-route.ts +3 -2
  90. package/src/proxy/route-auto-detection.test.ts +55 -0
  91. package/src/proxy/route-auto-detection.ts +85 -24
  92. package/src/proxy/route-proxy.test.ts +93 -0
  93. package/src/proxy/route-proxy.ts +94 -18
  94. package/src/server/context.test.ts +91 -15
  95. package/src/server/handlers/registry.handler.test.ts +103 -11
  96. package/src/server/handlers/registry.handler.ts +98 -11
  97. package/src/server/handlers/route.handler.test.ts +39 -0
  98. package/src/server/handlers/route.handler.ts +61 -58
  99. package/src/server/json-ld.tsx +51 -0
  100. package/src/server/layout.ts +1 -0
  101. package/src/server/lazy.ts +6 -7
  102. package/src/server/metadata.ts +43 -77
  103. package/src/server/mixer.ts +108 -132
  104. package/src/server/request-data.test.ts +37 -0
  105. package/src/server/session/session.ts +22 -17
  106. package/src/types.ts +6 -3
  107. package/dist/_virtual/rolldown_runtime.js +0 -27
  108. package/dist/node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.js +0 -76
  109. package/dist/node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.js.map +0 -1
  110. package/dist/node_modules/deepmerge/dist/cjs.js +0 -94
  111. package/dist/node_modules/deepmerge/dist/cjs.js.map +0 -1
  112. package/dist/node_modules/dom-serializer/lib/esm/foreignNames.js +0 -105
  113. package/dist/node_modules/dom-serializer/lib/esm/foreignNames.js.map +0 -1
  114. package/dist/node_modules/dom-serializer/lib/esm/index.js +0 -141
  115. package/dist/node_modules/dom-serializer/lib/esm/index.js.map +0 -1
  116. package/dist/node_modules/domelementtype/lib/esm/index.js +0 -53
  117. package/dist/node_modules/domelementtype/lib/esm/index.js.map +0 -1
  118. package/dist/node_modules/domhandler/lib/esm/index.js +0 -131
  119. package/dist/node_modules/domhandler/lib/esm/index.js.map +0 -1
  120. package/dist/node_modules/domhandler/lib/esm/node.js +0 -308
  121. package/dist/node_modules/domhandler/lib/esm/node.js.map +0 -1
  122. package/dist/node_modules/entities/lib/esm/decode.js +0 -364
  123. package/dist/node_modules/entities/lib/esm/decode.js.map +0 -1
  124. package/dist/node_modules/entities/lib/esm/decode_codepoint.js +0 -59
  125. package/dist/node_modules/entities/lib/esm/decode_codepoint.js.map +0 -1
  126. package/dist/node_modules/entities/lib/esm/escape.js +0 -93
  127. package/dist/node_modules/entities/lib/esm/escape.js.map +0 -1
  128. package/dist/node_modules/entities/lib/esm/generated/decode-data-html.js +0 -6
  129. package/dist/node_modules/entities/lib/esm/generated/decode-data-html.js.map +0 -1
  130. package/dist/node_modules/entities/lib/esm/generated/decode-data-xml.js +0 -6
  131. package/dist/node_modules/entities/lib/esm/generated/decode-data-xml.js.map +0 -1
  132. package/dist/node_modules/html-to-text/lib/html-to-text.js +0 -1828
  133. package/dist/node_modules/html-to-text/lib/html-to-text.js.map +0 -1
  134. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Parser.js +0 -435
  135. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Parser.js.map +0 -1
  136. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Tokenizer.js +0 -739
  137. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/Tokenizer.js.map +0 -1
  138. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/index.js +0 -20
  139. package/dist/node_modules/html-to-text/node_modules/htmlparser2/lib/esm/index.js.map +0 -1
  140. package/dist/node_modules/leac/lib/leac.js +0 -101
  141. package/dist/node_modules/leac/lib/leac.js.map +0 -1
  142. package/dist/node_modules/parseley/lib/parseley.js +0 -314
  143. package/dist/node_modules/parseley/lib/parseley.js.map +0 -1
  144. package/dist/node_modules/peberminta/lib/core.js +0 -149
  145. package/dist/node_modules/peberminta/lib/core.js.map +0 -1
  146. package/dist/node_modules/selderee/lib/selderee.js +0 -325
  147. package/dist/node_modules/selderee/lib/selderee.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @websolutespa/bom-next
2
2
 
3
+ ## 4.0.0-next.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Helpers no longer read request data; only the template does and passes it down.
8
+ - `getPageMetadata` / `getMetadata` read only `params` (no more `DYNAMIC_SERVER_USAGE` on static/ISR pages). A dynamic template awaits its own `searchParams` and calls `buildPageMetadata(page, layout, { searchParams })`.
9
+ - `getPayload` no longer calls `headers()`: in draft mode pass `headers` from the template.
10
+ - `draft` defaults to `false` in `createPageContext` and `createLayoutContext`; `'auto'` (reads `draftMode()`) is an explicit opt-in.
11
+ - Session helpers (`createSession`, `getSession`, `hasValidSession`, `updateSession`, `deleteSession`) take the cookie store as an argument.
12
+ - `detectedRoute` cookie values with dot segments (`..`, also percent-encoded) or backslashes are rejected.
13
+
14
+ - Updated dependencies
15
+ - @websolutespa/payload-plugin-bowl@4.0.0-next.3
16
+ - @websolutespa/payload-db-memory@3.0.3
17
+ - @websolutespa/payload-utils@4.0.0-next.0
18
+ - @websolutespa/bom-core@4.0.0-next.1
19
+
20
+ ## 4.0.0-next.3
21
+
22
+ ### Patch Changes
23
+
24
+ - Proxy: route resolutions are cached per instance (`BOM_ROUTE_CACHE_TTL` seconds, default 30; misses and redirects at most 10) with concurrent lookups shared. Draft requests and server errors are never cached.
25
+ - Proxy: the resolver query values (`pathname`, `href`, `hrefBeforeRedirect`) are now encoded, and CMS redirects go to the target and status returned by the resolver instead of always to `/`.
26
+ - `routeAutoDetection`: a valid `detectedRoute` cookie (relative paths only) redirects straight away without route, market or GeoIP lookups; GeoIP calls have a 2 s timeout and are cached per IP for one hour.
27
+ - Per-request diagnostics (proxy, local API calls, context cache) are logged in development only.
28
+ - Server context: `getRoutes` and `getMixerContext` read routes from the Bowl route store instead of re-reading every page collection and media document. Routes no longer depend on `market`, `locale` or `user`, and unpublished pages and category routes are not part of the public routes anymore.
29
+ - Server context: locale, market, category and route reads run in parallel; the result is cached per market, locale and user for `MEMORY_CACHE_DURATION` seconds and cleared as soon as the route store changes (also from other instances through Redis). Draft contexts are never cached, only shared within a request. The `_mixer` and `_route` globals are replaced by `_bomMixerContext`.
30
+ - `decorateLazyComponents` runs the lazy decorators in parallel, keeping the component order.
31
+ - Metadata: new `getPageMetadata`, `buildPageMetadata` and `inheritParentMetadata` build plain-text title and description, canonical (keeping `pagination[page]`), `alternates.languages` with `x-default`, robots, keywords, Open Graph and Twitter on the server. `getMetadata` is now a wrapper of `getPageMetadata`, so its result contains all of them instead of title and description only; Open Graph and Twitter fields left empty by the page are inherited from the parent metadata.
32
+ - Metadata: `JsonLd` and `buildPageStructuredData` render JSON-LD in Server Components.
33
+ - `MetaDataProvider` no longer uses `next/compat/router` (pass the query string through the new `search` prop), takes `brand.url` from `layout.homeHref`, reads `meta.robots` or `meta.robot`, converts HTML to text without bullets, link URLs or uppercased headings, and uses uppercase hreflang countries (`it-IT`).
34
+ - `layout.homeHref` exposes the root page href for the current market and locale.
35
+ - `html-to-text` and `schema-dts` are declared as dependencies.
36
+ - Registry: aliases are read from `components.json` (or `UI_REGISTRY_COMPONENTS_JSON` and `UI_REGISTRY_SOURCE_ALIAS`) and rewritten to registry imports; only import and export specifiers are rewritten. Items that import app code the registry cannot ship are reported in `meta.unresolvedImports`, or hidden with `UI_REGISTRY_SKIP_UNRESOLVED`.
37
+ - `UserProvider` reads `/auth/me` once per browser session and accepts `initialUser`; `invalidateUser()` forces a new read.
38
+ - Updated dependencies
39
+ - @websolutespa/payload-plugin-bowl@4.0.0-next.2
40
+ - @websolutespa/bom-core@4.0.0-next.1
41
+
3
42
  ## 4.0.0-next.2
4
43
 
5
44
  ### Patch Changes
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
- # @websolutespa/bom-next
2
-
3
- [![npm version](https://badge.fury.io/js/%40websolutespa%2Fbom-next.svg)](https://badge.fury.io/js/%40websolutespa%2Fbom-next)
4
-
5
- [![status alpha](https://img.shields.io/badge/status-alpha-red.svg)](https://shields.io/)
6
-
7
- Next module of the [BOM Repository](https://github.com/websolutespa/bom) by [websolute](https://www.websolute.com/).
8
-
9
- ---
10
-
11
- ##### *this library is for internal usage and not production ready*
1
+ # @websolutespa/bom-next
2
+
3
+ [![npm version](https://badge.fury.io/js/%40websolutespa%2Fbom-next.svg)](https://badge.fury.io/js/%40websolutespa%2Fbom-next)
4
+
5
+ [![status alpha](https://img.shields.io/badge/status-alpha-red.svg)](https://shields.io/)
6
+
7
+ Next module of the [BOM Repository](https://github.com/websolutespa/bom) by [websolute](https://www.websolute.com/).
8
+
9
+ ---
10
+
11
+ ##### *this library is for internal usage and not production ready*
12
12
 
13
13
  ## Page context
14
14
 
@@ -37,3 +37,53 @@ const { layout } = await createLayoutContext(market, locale, { config }); // dep
37
37
 
38
38
  Page and layout reads are memoized per request, so `getMetadata` and the page component share them when called with the same options.
39
39
  To bind `config` once: `export const { createPageContext, createLayoutContext, getMetadata } = createContextHelpers({ config, defaults: { depth: 2 } });`
40
+
41
+ ## Metadata
42
+
43
+ `getPageMetadata` (and `getMetadata`, its `config`-first form) returns a `generateMetadata` function that renders on the server: plain-text title and description, canonical (keeping `?pagination[page]=N` after the first page), `alternates.languages` with one `locale-COUNTRY` entry per market country plus `x-default`, robots, keywords, Open Graph and Twitter.
44
+
45
+ ```ts
46
+ import { getPageMetadata, JsonLd, buildPageStructuredData } from '@websolutespa/bom-next/server';
47
+
48
+ export const generateMetadata = getPageMetadata('post_detail', {
49
+ config,
50
+ metadata: {
51
+ brand: { name: 'Example', logo: '/logo.png', twitterName: '@example' },
52
+ extend: (metadata, { page }) => ({ ...metadata, title: `${metadata.title} | Example` }),
53
+ },
54
+ });
55
+
56
+ // JSON-LD in the page component
57
+ <JsonLd data={buildPageStructuredData(page, layout, { brand: { name: 'Example' } })} />
58
+ ```
59
+
60
+ `buildPageMetadata(page, layout, options)` is the same builder for custom routes. URLs are resolved against `NEXT_PUBLIC_URL` unless `baseUrl` is passed. Robots come from `meta.robots`, or `meta.robot` (the `@payloadcms/plugin-seo` field).
61
+
62
+ The client `MetaDataProvider` only computes values for `useMetaData`; the `next/head` based `Meta`, `OpenGraph` and `StructuredData` components of the mixer packages render nothing in the App Router.
63
+
64
+ `layout.homeHref` is the root page href for the current market and locale; prefer it to `layout.topLevelHrefs.<slug>`, which is keyed by collection slug or template.
65
+
66
+ ## Routing proxy
67
+
68
+ `createRouteProxy` resolves each request through `/api/-route` and caches the result per instance: found routes for `BOM_ROUTE_CACHE_TTL` seconds (default `30`, `0` disables), misses and redirects for at most 10 seconds. Draft requests and server errors are never cached. CMS redirects send the browser to the target returned by the resolver.
69
+
70
+ `routeAutoDetection` redirects `/` straight from the `detectedRoute` cookie when present; otherwise it detects the market (GeoIP, 2 s timeout, cached per IP for one hour) and the locale.
71
+
72
+ The server context (`getContext`) reads routes from the Bowl route store and caches the rest per market, locale and user for `MEMORY_CACHE_DURATION` seconds (default 300). Any route store change, local or from another instance through Redis, clears it. Draft contexts are never cached.
73
+
74
+ ## Environment
75
+
76
+ Per-request diagnostics (proxy, local API calls, context cache) are logged in development only (`NODE_ENV` unset or `development`).
77
+
78
+ | Variable | Default | |
79
+ |---|---|---|
80
+ | `BOM_ROUTE_CACHE_TTL` | `30` | proxy route cache lifetime in seconds, `0` disables it |
81
+ | `MEMORY_CACHE_DURATION` | `300` | server context cache lifetime in seconds |
82
+ | `UI_REGISTRY_SOURCE_DIR` | `packages/ui/src` | registry source directory |
83
+ | `UI_REGISTRY_COMPONENTS_JSON` | nearest `components.json` | shadcn config whose aliases (e.g. `@/ui/lib/utils`) are mapped to registry imports |
84
+ | `UI_REGISTRY_SOURCE_ALIAS` | – | extra import prefix pointing at the registry source, e.g. `@/ui/` |
85
+ | `UI_REGISTRY_SKIP_UNRESOLVED` | – | `1` hides registry items that import app code; otherwise they are listed with `meta.unresolvedImports` |
86
+
87
+ ## User
88
+
89
+ `UserProvider` reads `/api/auth/me` once per page load, shared by every mount and client navigation. Pass `initialUser` (a user or `null`) when the server already knows it to skip the request; `setUser` and `invalidateUser()` keep the cached value current after login or logout.
@@ -1,7 +1,7 @@
1
- import * as react8 from "react";
1
+ import * as react1 from "react";
2
2
 
3
3
  //#region src/client/client.d.ts
4
- declare function Client(): react8.JSX.Element;
4
+ declare function Client(): react1.JSX.Element;
5
5
  //# sourceMappingURL=client.d.ts.map
6
6
 
7
7
  //#endregion
@@ -1,15 +1,15 @@
1
- import * as react4 from "react";
1
+ import * as react0 from "react";
2
2
 
3
3
  //#region src/client/extra.d.ts
4
4
  type IExtraContext = Record<string, any>;
5
- declare const ExtraContext: react4.Context<IExtraContext | undefined>;
5
+ declare const ExtraContext: react0.Context<IExtraContext | undefined>;
6
6
  declare function ExtraProvider({
7
7
  extra,
8
8
  children
9
9
  }: {
10
10
  extra?: Record<string, any>;
11
11
  children?: React.ReactNode;
12
- }): react4.JSX.Element;
12
+ }): react0.JSX.Element;
13
13
  //# sourceMappingURL=extra.d.ts.map
14
14
 
15
15
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"extra.d.ts","names":[],"sources":["../../src/client/extra.tsx"],"sourcesContent":[],"mappings":";;;KAIY,aAAA,GAAgB;cAEf,cAAY,MAAA,CAAA,QAAA;iBAET,aAAA;;;;EAJJ,KAAA,CAAA,EAQF,MARE,CAAA,MAAa,EAAA,GAAA,CAAA;EAEZ,QAAA,CAAA,EAOA,KAAA,CAAM,SAP4D;CAAA,CAAA,EAQ9E,MAAA,CAAA,GAAA,CAAA,OAR8E"}
1
+ {"version":3,"file":"extra.d.ts","names":[],"sources":["../../src/client/extra.tsx"],"sourcesContent":[],"mappings":";;;KAIY,aAAA,GAAgB;cAEf,cAAY,MAAA,CAAA,QAAA;iBAET,aAAA;;;;EAJJ,KAAA,CAAA,EAQF,MARE,CAAA,MAAa,EAAA,GAAA,CAAG;EAEf,QAAA,CAAA,EAOA,KAAA,CAAM,SAP4D;CAAA,CAAA,EAQ9E,MAAA,CAAA,GAAA,CAAA,OAR8E"}
@@ -1,13 +1,13 @@
1
- import * as react9 from "react";
1
+ import * as react2 from "react";
2
2
 
3
3
  //#region src/client/label.d.ts
4
4
  type ILabelContext = (key: string, params?: Record<string, any>) => string;
5
- declare const LabelContext: react9.Context<ILabelContext | undefined>;
5
+ declare const LabelContext: react2.Context<ILabelContext | undefined>;
6
6
  declare function LabelProvider({
7
7
  children
8
8
  }: {
9
9
  children?: React.ReactNode;
10
- }): react9.JSX.Element;
10
+ }): react2.JSX.Element;
11
11
  //# sourceMappingURL=label.d.ts.map
12
12
 
13
13
  //#endregion
@@ -1,16 +1,16 @@
1
1
  import { ILayout } from "@websolutespa/bom-core";
2
- import * as react13 from "react";
2
+ import * as react4 from "react";
3
3
 
4
4
  //#region src/client/layout.d.ts
5
5
  type ILayoutContext = ILayout;
6
- declare const LayoutContext: react13.Context<ILayoutContext | undefined>;
6
+ declare const LayoutContext: react4.Context<ILayoutContext | undefined>;
7
7
  declare function LayoutProvider({
8
8
  layout,
9
9
  children
10
10
  }: {
11
11
  layout: ILayout;
12
12
  children?: React.ReactNode;
13
- }): react13.JSX.Element;
13
+ }): react4.JSX.Element;
14
14
  //# sourceMappingURL=layout.d.ts.map
15
15
 
16
16
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","names":[],"sources":["../../src/client/layout.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,cAAA,GAAiB;cAEhB,eAAa,OAAA,CAAA,QAAA;iBAEV,cAAA;;;;EAJJ,MAAA,EAQF,OARE;EAEC,QAAA,CAAA,EAOA,KAAA,CAAM,SAP8D;CAAA,CAAA,EAQhF,OAAA,CAAA,GAAA,CAAA,OARgF"}
1
+ {"version":3,"file":"layout.d.ts","names":[],"sources":["../../src/client/layout.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,cAAA,GAAiB;cAEhB,eAAa,MAAA,CAAA,QAAA;iBAEV,cAAA;;;;EAJJ,MAAA,EAQF,OARE;EAEC,QAAA,CAAA,EAOA,KAAA,CAAM,SAP8D;CAAA,CAAA,EAQhF,MAAA,CAAA,GAAA,CAAA,OARgF"}
@@ -30,11 +30,21 @@ type IMetaDataOptions = {
30
30
  };
31
31
  type IMetaDataContext = IMetaData;
32
32
  declare const MetaDataContext: react6.Context<IMetaData | undefined>;
33
+ /**
34
+ * Computes page metadata for client components (`useMetaData`). It renders
35
+ * nothing into `<head>`: in the App Router, export `generateMetadata` built
36
+ * with `getPageMetadata` from `@websolutespa/bom-next/server` instead.
37
+ *
38
+ * `search` is the current query string (e.g. `?pagination[page]=2`); pages
39
+ * after the first keep their pagination in the canonical URL.
40
+ */
33
41
  declare function MetaDataProvider({
34
42
  metaData,
43
+ search,
35
44
  children
36
45
  }: {
37
46
  metaData: IMetaDataOptions;
47
+ search?: string;
38
48
  children?: ReactNode;
39
49
  }): react6.JSX.Element;
40
50
  //# sourceMappingURL=metadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","names":[],"sources":["../../src/client/metadata.tsx"],"sourcesContent":[],"mappings":";;;;;;;;KAeY,gBAAA;;;IAAA,IAAA,EAAA,MAAA;IAAgB,YAAA,CAAA,EAAA,MAAA;IAgBf,OAAA,CAAA,EAAA,MAAA;IAEM,GAAA,CAAA,EAAA,MAAA;IACL,KAAA,CAAA,EAAA,MAAA;IACE,SAAA,CAAA,EAAA,MAAA;IAAe,OAAA,CAAA,EAAA,MAAA;IAGnB,OAAA,CAAA,EAAA,MAAgB;IAEf,IAAA,CAAA,EAAA,MAAA;IAAwE,YAAA,CAAA,EAAA,MAAA;IAAzD,WAAA,CAAA,EAAA,MAAA;IAAA,WAAA,CAAA,EAAA,MAAA;IAAA,WAAA,CAAA,EAAA,MAAA;IAEZ,MAAA,CAAA,EAXH,MAWmB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAA,CAAA;EAAA,cAC9B,CAAA,EAViB,kBAUjB;EAAQ,SACR,CAAA,EAVY,aAUZ;EAAQ,WAEE,CAAA,EAXI,eAWJ;CAAgB;AAE3B,KAVW,gBAAA,GAAmB,SAU9B;AAAA,cARY,eAQZ,EAR2B,MAAA,CAAA,OAQ3B,CAR2B,SAQ3B,GAAA,SAAA,CAAA;iBANe,gBAAA;;;;YAIJ;aACC;IACZ,MAAA,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"metadata.d.ts","names":[],"sources":["../../src/client/metadata.tsx"],"sourcesContent":[],"mappings":";;;;;;;;KAcY,gBAAA;;;IAAA,IAAA,EAAA,MAAA;IAAgB,YAAA,CAAA,EAAA,MAAA;IAgBf,OAAA,CAAA,EAAA,MAAA;IAEM,GAAA,CAAA,EAAA,MAAA;IACL,KAAA,CAAA,EAAA,MAAA;IACE,SAAA,CAAA,EAAA,MAAA;IAAe,OAAA,CAAA,EAAA,MAAA;IAGnB,OAAA,CAAA,EAAA,MAAgB;IAEf,IAAA,CAAA,EAAA,MAAA;IAAwE,YAAA,CAAA,EAAA,MAAA;IAAzD,WAAA,CAAA,EAAA,MAAA;IAAA,WAAA,CAAA,EAAA,MAAA;IAAA,WAAA,CAAA,EAAA,MAAA;IAUZ,MAAA,CAAA,EAnBH,MAmBmB,CAAA,MAAA,EAAA,MAAA,CAAA;EAAA,CAAA;EAAA,cAC9B,CAAA,EAlBiB,kBAkBjB;EAAQ,SACR,CAAA,EAlBY,aAkBZ;EAAM,WACN,CAAA,EAlBc,eAkBd;CAAQ;AAIG,KAnBD,gBAAA,GAAmB,SAmBlB;AACZ,cAlBY,eAkBZ,EAlB2B,MAAA,CAAA,OAkB3B,CAlB2B,SAkB3B,GAAA,SAAA,CAAA;AAAA;;;;;;;;iBARe,gBAAA;;;;;YAKJ;;aAEC;IACZ,MAAA,CAAA,GAAA,CAAA"}
@@ -4,72 +4,53 @@
4
4
  import { getPublicUrl, resolveHref } from "../lib/utils.js";
5
5
  import { useLayout } from "./useLayout.js";
6
6
  import { usePage } from "./usePage.js";
7
- import { convert } from "../node_modules/html-to-text/lib/html-to-text.js";
8
- import { defaultLocale, defaultMarket } from "@websolutespa/bom-core";
7
+ import { getPageAlternates, getPageXDefault, getPaginationQuery, toPlainText } from "../lib/page-metadata.js";
8
+ import { getMetaRobots } from "@websolutespa/bom-core";
9
9
  import { createContext, useMemo } from "react";
10
10
  import { jsx } from "react/jsx-runtime";
11
- import { useRouter } from "next/compat/router";
12
11
 
13
12
  //#region src/client/metadata.tsx
14
13
  const MetaDataContext = createContext(void 0);
15
- function MetaDataProvider({ metaData, children }) {
16
- const router = useRouter();
14
+ /**
15
+
16
+ * Computes page metadata for client components (`useMetaData`). It renders
17
+
18
+ * nothing into `<head>`: in the App Router, export `generateMetadata` built
19
+
20
+ * with `getPageMetadata` from `@websolutespa/bom-next/server` instead.
21
+
22
+ *
23
+
24
+ * `search` is the current query string (e.g. `?pagination[page]=2`); pages
25
+
26
+ * after the first keep their pagination in the canonical URL.
27
+
28
+ */
29
+ function MetaDataProvider({ metaData, search, children }) {
17
30
  const layout = useLayout();
18
31
  const page = usePage();
32
+ const homeHref = layout.homeHref ?? layout.tree?.href;
19
33
  const brand = {
20
34
  ...metaData.brand,
21
35
  logo: resolveHref(metaData.brand.logo),
22
- url: getPublicUrl(),
36
+ url: metaData.brand.url || (homeHref ? resolveHref(homeHref) : getPublicUrl()),
23
37
  businessName: metaData.brand.businessName || metaData.brand.name,
24
38
  appName: metaData.brand.appName || metaData.brand.name
25
39
  };
26
- const title = convert(page.meta?.title || page.title || "");
27
- const description = convert(page.meta?.description || page.abstract || "");
40
+ const title = toPlainText(page.meta?.title || page.title || "");
41
+ const description = toPlainText(page.meta?.description || page.abstract || "");
28
42
  const media = page.meta?.image || page.media;
29
43
  const image = media ? resolveHref(media.src || media.url) : brand.logo;
30
- let canonical = resolveHref(page.href);
31
- const asPath = router?.asPath || "";
32
- const queryIndex = asPath.indexOf("?");
33
- const query = queryIndex !== -1 ? asPath.substring(queryIndex) : "";
34
- if (query.match(/^\?pagination\[page\]=(\d+)$/) && query !== "?pagination[page]=1") canonical = `${canonical}${query}`;
35
- const url = `${canonical}${query}`;
36
- const alternates = useMemo(() => {
37
- const alternates$1 = [];
38
- (page.alternates || []).forEach((alternate) => {
39
- const market = layout.markets.find((x) => x.id === alternate.market);
40
- if (market) if (market.countries && market.countries.length > 0) market.countries.forEach((country) => {
41
- const alpha2Code = typeof country === "string" ? country : country.alpha2Code;
42
- alternates$1.push({
43
- id: `${alternate.market}-${alternate.locale}-${alpha2Code}`,
44
- href: resolveHref(alternate.id),
45
- hrefLang: `${alternate.locale}-${alpha2Code}`
46
- });
47
- });
48
- else alternates$1.push({
49
- id: `${alternate.market}-${alternate.locale}`,
50
- href: resolveHref(alternate.id),
51
- hrefLang: alternate.locale
52
- });
53
- });
54
- return alternates$1;
55
- }, [layout, page]);
56
- const xDefault = useMemo(() => {
57
- const xDefaultMarket = layout.markets.find((x) => x.isDefault)?.id || defaultMarket;
58
- const xDefaultLocale = layout.locales.find((x) => x.isDefault)?.id || defaultLocale;
59
- const alternate = page.alternates.find((x) => x.market === xDefaultMarket && x.locale === xDefaultLocale);
60
- if (alternate) return {
61
- id: `${alternate.market}-${alternate.locale}`,
62
- href: resolveHref(alternate.id),
63
- hrefLang: alternate.locale
64
- };
65
- return void 0;
66
- }, [layout, page]);
44
+ const canonical = `${resolveHref(page.href)}${getPaginationQuery(new URLSearchParams(search || ""))}`;
45
+ const url = canonical;
46
+ const alternates = useMemo(() => getPageAlternates(page, layout, getPublicUrl()), [layout, page]);
47
+ const xDefault = useMemo(() => getPageXDefault(page, layout, getPublicUrl()), [layout, page]);
67
48
  const meta = {
68
49
  title,
69
50
  description,
70
51
  image,
71
52
  keywords: page.meta?.keywords,
72
- robots: page.meta?.robots,
53
+ robots: getMetaRobots(page.meta),
73
54
  slug: page.href,
74
55
  url,
75
56
  canonical,
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","names":["brand: IBrand","htmlToText","alternates: IAlternate[]","alternate: IRoute","country: any","alternates","meta: IMeta","contactPoint: ContactPoint","postalAddress: PostalAddress","organization: Organization","structuredData: StructuredDataType","openGraph: OpenGraphType","twitterCard: TwitterCardType | undefined","value: IMetaData"],"sources":["../../src/client/metadata.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { defaultLocale, defaultMarket, IMedia, IRoute } from '@websolutespa/bom-core';\r\nimport { convert as htmlToText } from 'html-to-text';\r\nimport { useRouter } from 'next/compat/router';\r\nimport { createContext, ReactNode, useMemo } from 'react';\r\nimport { ContactPoint, Organization, PostalAddress } from 'schema-dts';\r\nimport { IAlternate, IBrand, IMeta, IMetaData } from '../lib/meta-data';\r\nimport { OpenGraphType } from '../lib/open-graph';\r\nimport { StructuredDataType } from '../lib/structured-data';\r\nimport { TwitterCardType } from '../lib/twitter-card';\r\nimport { getPublicUrl, resolveHref } from '../lib/utils';\r\nimport { useLayout } from './useLayout';\r\nimport { usePage } from './usePage';\r\n\r\nexport type IMetaDataOptions = {\r\n brand: {\r\n name: string;\r\n logo: string;\r\n businessName?: string;\r\n appName?: string;\r\n url?: string;\r\n email?: string;\r\n telephone?: string;\r\n zipCode?: string;\r\n address?: string;\r\n city?: string;\r\n provinceCode?: string;\r\n countryName?: string;\r\n countryCode?: string;\r\n twitterName?: string;\r\n social?: Record<string, string>;\r\n },\r\n structuredData?: StructuredDataType;\r\n openGraph?: OpenGraphType;\r\n twitterCard?: TwitterCardType;\r\n};\r\n\r\nexport type IMetaDataContext = IMetaData;\r\n\r\nexport const MetaDataContext = createContext<IMetaDataContext | undefined>(undefined);\r\n\r\nexport function MetaDataProvider({\r\n metaData,\r\n children,\r\n}: {\r\n metaData: IMetaDataOptions,\r\n children?: ReactNode,\r\n}) {\r\n const router = useRouter();\r\n const layout = useLayout();\r\n const page = usePage();\r\n const brand: IBrand = {\r\n ...metaData.brand,\r\n logo: resolveHref(metaData.brand.logo),\r\n url: getPublicUrl(), // layout.topLevelHrefs.homepage,\r\n businessName: metaData.brand.businessName || metaData.brand.name,\r\n appName: metaData.brand.appName || metaData.brand.name,\r\n };\r\n const title = htmlToText(page.meta?.title || page.title || '');\r\n const description = htmlToText(page.meta?.description || page.abstract || '');\r\n const media = ((page.meta as { image: IMedia } | undefined)?.image || page.media);\r\n const image = media ? resolveHref(media.src || media.url) : brand.logo;\r\n let canonical = resolveHref(page.href);\r\n const asPath = router?.asPath || '';\r\n const queryIndex = asPath.indexOf('?');\r\n const query = queryIndex !== -1 ? asPath.substring(queryIndex) : '';\r\n if (query.match(/^\\?pagination\\[page\\]=(\\d+)$/) && query !== '?pagination[page]=1') {\r\n canonical = `${canonical}${query}`;\r\n // title = `${title} - ${query.replace('?pagination[page]=','')}`;\r\n }\r\n const url = `${canonical}${query}`;\r\n const alternates = useMemo(() => {\r\n const alternates: IAlternate[] = [];\r\n (page.alternates || []).forEach((alternate: IRoute) => {\r\n const market = layout.markets.find(x => x.id === alternate.market);\r\n if (market) {\r\n if (market.countries && market.countries.length > 0) {\r\n market.countries.forEach((country: any) => {\r\n // country: string | { alpha2Code: string };\r\n const alpha2Code = typeof country === 'string' ? country : country.alpha2Code;\r\n alternates.push({\r\n id: `${alternate.market}-${alternate.locale}-${alpha2Code}`,\r\n href: resolveHref(alternate.id),\r\n hrefLang: `${alternate.locale}-${alpha2Code}`,\r\n });\r\n });\r\n } else {\r\n alternates.push({\r\n id: `${alternate.market}-${alternate.locale}`,\r\n href: resolveHref(alternate.id),\r\n hrefLang: alternate.locale,\r\n });\r\n }\r\n }\r\n });\r\n return alternates;\r\n }, [layout, page]);\r\n const xDefault = useMemo(() => {\r\n const xDefaultMarket = layout.markets.find(x => x.isDefault)?.id || defaultMarket;\r\n const xDefaultLocale = layout.locales.find(x => x.isDefault)?.id || defaultLocale;\r\n const alternate = page.alternates.find(x => x.market === xDefaultMarket && x.locale === xDefaultLocale);\r\n if (alternate) {\r\n return {\r\n id: `${alternate.market}-${alternate.locale}`,\r\n href: resolveHref(alternate.id),\r\n hrefLang: alternate.locale,\r\n };\r\n }\r\n return undefined;\r\n }, [layout, page]);\r\n const meta: IMeta = {\r\n title: title,\r\n description: description,\r\n image: image,\r\n keywords: page.meta?.keywords,\r\n robots: page.meta?.robots,\r\n slug: page.href,\r\n url,\r\n canonical,\r\n alternates,\r\n xDefault,\r\n };\r\n const contactPoint: ContactPoint = {\r\n '@type': 'ContactPoint',\r\n telephone: brand.telephone,\r\n email: brand.email,\r\n contactType: 'customer service',\r\n areaServed: layout.locales.map(x => x.id),\r\n availableLanguage: layout.locales.map(x => x.id),\r\n };\r\n const postalAddress: PostalAddress = {\r\n '@type': 'PostalAddress',\r\n streetAddress: brand.address,\r\n addressLocality: brand.city,\r\n postalCode: brand.zipCode,\r\n addressRegion: brand.provinceCode,\r\n addressCountry: brand.countryCode,\r\n };\r\n const organization: Organization = {\r\n '@type': 'Organization',\r\n name: brand.businessName,\r\n logo: {\r\n '@type': 'ImageObject',\r\n url: brand.logo,\r\n },\r\n url: brand.url,\r\n /*\r\n description,\r\n founder,\r\n foundingDate,\r\n numberOfEmployees,\r\n */\r\n /*\r\n foundingDate: '2020-01-01',\r\n numberOfEmployees: {\r\n '@type': 'QuantitativeValue',\r\n value: 150,\r\n },\r\n */\r\n };\r\n const hasContact = Boolean(brand.email || brand.telephone);\r\n if (hasContact) {\r\n organization.contactPoint = contactPoint;\r\n }\r\n const hasAddress = Boolean(brand.address || brand.city || brand.zipCode || brand.provinceCode || brand.countryCode);\r\n if (hasAddress) {\r\n organization.address = postalAddress;\r\n }\r\n if (brand.social) {\r\n organization.sameAs = Object.values(brand.social);\r\n }\r\n const structuredData: StructuredDataType = metaData.structuredData || {\r\n '@type': 'WebPage',\r\n name: title,\r\n url: url,\r\n description: description,\r\n publisher: organization,\r\n };\r\n const openGraph: OpenGraphType = metaData.openGraph || {\r\n type: 'website',\r\n title: title,\r\n description: description,\r\n url: url,\r\n siteName: brand.name,\r\n image: image,\r\n locale: layout.locale,\r\n };\r\n const twitterCard: TwitterCardType | undefined = metaData.twitterCard || (\r\n brand.twitterName ? {\r\n card: 'summary_large_image',\r\n title: title,\r\n description: description,\r\n image: image,\r\n imageAlt: brand.businessName,\r\n site: brand.twitterName,\r\n creator: brand.twitterName,\r\n } : undefined\r\n );\r\n const value: IMetaData = {\r\n layout,\r\n page,\r\n brand,\r\n contactPoint,\r\n meta,\r\n openGraph,\r\n organization,\r\n postalAddress,\r\n structuredData,\r\n twitterCard,\r\n };\r\n return (\r\n <MetaDataContext.Provider value={value}>\r\n {children}\r\n </MetaDataContext.Provider>\r\n );\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"metadata.js","names":["brand: IBrand","meta: IMeta","contactPoint: ContactPoint","postalAddress: PostalAddress","organization: Organization","structuredData: StructuredDataType","openGraph: OpenGraphType","twitterCard: TwitterCardType | undefined","value: IMetaData"],"sources":["../../src/client/metadata.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { getMetaRobots, IMedia } from '@websolutespa/bom-core';\r\nimport { createContext, ReactNode, useMemo } from 'react';\r\nimport { ContactPoint, Organization, PostalAddress } from 'schema-dts';\r\nimport { IBrand, IMeta, IMetaData } from '../lib/meta-data';\r\nimport { OpenGraphType } from '../lib/open-graph';\r\nimport { StructuredDataType } from '../lib/structured-data';\r\nimport { TwitterCardType } from '../lib/twitter-card';\r\nimport { getPageAlternates, getPageXDefault, getPaginationQuery, toPlainText } from '../lib/page-metadata';\r\nimport { getPublicUrl, resolveHref } from '../lib/utils';\r\nimport { useLayout } from './useLayout';\r\nimport { usePage } from './usePage';\r\n\r\nexport type IMetaDataOptions = {\r\n brand: {\r\n name: string;\r\n logo: string;\r\n businessName?: string;\r\n appName?: string;\r\n url?: string;\r\n email?: string;\r\n telephone?: string;\r\n zipCode?: string;\r\n address?: string;\r\n city?: string;\r\n provinceCode?: string;\r\n countryName?: string;\r\n countryCode?: string;\r\n twitterName?: string;\r\n social?: Record<string, string>;\r\n },\r\n structuredData?: StructuredDataType;\r\n openGraph?: OpenGraphType;\r\n twitterCard?: TwitterCardType;\r\n};\r\n\r\nexport type IMetaDataContext = IMetaData;\r\n\r\nexport const MetaDataContext = createContext<IMetaDataContext | undefined>(undefined);\r\n\r\n/**\r\n * Computes page metadata for client components (`useMetaData`). It renders\r\n * nothing into `<head>`: in the App Router, export `generateMetadata` built\r\n * with `getPageMetadata` from `@websolutespa/bom-next/server` instead.\r\n *\r\n * `search` is the current query string (e.g. `?pagination[page]=2`); pages\r\n * after the first keep their pagination in the canonical URL.\r\n */\r\nexport function MetaDataProvider({\r\n metaData,\r\n search,\r\n children,\r\n}: {\r\n metaData: IMetaDataOptions,\r\n search?: string,\r\n children?: ReactNode,\r\n}) {\r\n const layout = useLayout();\r\n const page = usePage();\r\n const homeHref = layout.homeHref ?? layout.tree?.href;\r\n const brand: IBrand = {\r\n ...metaData.brand,\r\n logo: resolveHref(metaData.brand.logo),\r\n url: metaData.brand.url || (homeHref ? resolveHref(homeHref) : getPublicUrl()),\r\n businessName: metaData.brand.businessName || metaData.brand.name,\r\n appName: metaData.brand.appName || metaData.brand.name,\r\n };\r\n const title = toPlainText(page.meta?.title || page.title || '');\r\n const description = toPlainText(page.meta?.description || page.abstract || '');\r\n const media = ((page.meta as { image: IMedia } | undefined)?.image || page.media);\r\n const image = media ? resolveHref(media.src || media.url) : brand.logo;\r\n const canonical = `${resolveHref(page.href)}${getPaginationQuery(new URLSearchParams(search || ''))}`;\r\n const url = canonical;\r\n const alternates = useMemo(() => getPageAlternates(page, layout, getPublicUrl()), [layout, page]);\r\n const xDefault = useMemo(() => getPageXDefault(page, layout, getPublicUrl()), [layout, page]);\r\n const meta: IMeta = {\r\n title: title,\r\n description: description,\r\n image: image,\r\n keywords: page.meta?.keywords,\r\n robots: getMetaRobots(page.meta),\r\n slug: page.href,\r\n url,\r\n canonical,\r\n alternates,\r\n xDefault,\r\n };\r\n const contactPoint: ContactPoint = {\r\n '@type': 'ContactPoint',\r\n telephone: brand.telephone,\r\n email: brand.email,\r\n contactType: 'customer service',\r\n areaServed: layout.locales.map(x => x.id),\r\n availableLanguage: layout.locales.map(x => x.id),\r\n };\r\n const postalAddress: PostalAddress = {\r\n '@type': 'PostalAddress',\r\n streetAddress: brand.address,\r\n addressLocality: brand.city,\r\n postalCode: brand.zipCode,\r\n addressRegion: brand.provinceCode,\r\n addressCountry: brand.countryCode,\r\n };\r\n const organization: Organization = {\r\n '@type': 'Organization',\r\n name: brand.businessName,\r\n logo: {\r\n '@type': 'ImageObject',\r\n url: brand.logo,\r\n },\r\n url: brand.url,\r\n /*\r\n description,\r\n founder,\r\n foundingDate,\r\n numberOfEmployees,\r\n */\r\n /*\r\n foundingDate: '2020-01-01',\r\n numberOfEmployees: {\r\n '@type': 'QuantitativeValue',\r\n value: 150,\r\n },\r\n */\r\n };\r\n const hasContact = Boolean(brand.email || brand.telephone);\r\n if (hasContact) {\r\n organization.contactPoint = contactPoint;\r\n }\r\n const hasAddress = Boolean(brand.address || brand.city || brand.zipCode || brand.provinceCode || brand.countryCode);\r\n if (hasAddress) {\r\n organization.address = postalAddress;\r\n }\r\n if (brand.social) {\r\n organization.sameAs = Object.values(brand.social);\r\n }\r\n const structuredData: StructuredDataType = metaData.structuredData || {\r\n '@type': 'WebPage',\r\n name: title,\r\n url: url,\r\n description: description,\r\n publisher: organization,\r\n };\r\n const openGraph: OpenGraphType = metaData.openGraph || {\r\n type: 'website',\r\n title: title,\r\n description: description,\r\n url: url,\r\n siteName: brand.name,\r\n image: image,\r\n locale: layout.locale,\r\n };\r\n const twitterCard: TwitterCardType | undefined = metaData.twitterCard || (\r\n brand.twitterName ? {\r\n card: 'summary_large_image',\r\n title: title,\r\n description: description,\r\n image: image,\r\n imageAlt: brand.businessName,\r\n site: brand.twitterName,\r\n creator: brand.twitterName,\r\n } : undefined\r\n );\r\n const value: IMetaData = {\r\n layout,\r\n page,\r\n brand,\r\n contactPoint,\r\n meta,\r\n openGraph,\r\n organization,\r\n postalAddress,\r\n structuredData,\r\n twitterCard,\r\n };\r\n return (\r\n <MetaDataContext.Provider value={value}>\r\n {children}\r\n </MetaDataContext.Provider>\r\n );\r\n}\r\n"],"mappings":";;;;;;;;;;;;AAuCA,MAAa,kBAAkB,cAA4C,OAAU;;;;;;;;;;;;;;;;AAUrF,SAAgB,iBAAiB,EAC/B,UACA,QACA,UAKD,EAAE;CACD,MAAM,SAAS,WAAW;CAC1B,MAAM,OAAO,SAAS;CACtB,MAAM,WAAW,OAAO,YAAY,OAAO,MAAM;CACjD,MAAMA,QAAgB;EACpB,GAAG,SAAS;EACZ,MAAM,YAAY,SAAS,MAAM,KAAK;EACtC,KAAK,SAAS,MAAM,QAAQ,WAAW,YAAY,SAAS,GAAG,cAAc;EAC7E,cAAc,SAAS,MAAM,gBAAgB,SAAS,MAAM;EAC5D,SAAS,SAAS,MAAM,WAAW,SAAS,MAAM;CACnD;CACD,MAAM,QAAQ,YAAY,KAAK,MAAM,SAAS,KAAK,SAAS,GAAG;CAC/D,MAAM,cAAc,YAAY,KAAK,MAAM,eAAe,KAAK,YAAY,GAAG;CAC9E,MAAM,QAAU,KAAK,MAAwC,SAAS,KAAK;CAC3E,MAAM,QAAQ,QAAQ,YAAY,MAAM,OAAO,MAAM,IAAI,GAAG,MAAM;CAClE,MAAM,YAAY,GAAG,YAAY,KAAK,KAAK,GAAG,mBAAmB,IAAI,gBAAgB,UAAU,IAAI,EAAE;CACrG,MAAM,MAAM;CACZ,MAAM,aAAa,QAAQ,MAAM,kBAAkB,MAAM,QAAQ,cAAc,CAAC,EAAE,CAAC,QAAQ,IAAK,EAAC;CACjG,MAAM,WAAW,QAAQ,MAAM,gBAAgB,MAAM,QAAQ,cAAc,CAAC,EAAE,CAAC,QAAQ,IAAK,EAAC;CAC7F,MAAMC,OAAc;EACX;EACM;EACN;EACP,UAAU,KAAK,MAAM;EACrB,QAAQ,cAAc,KAAK,KAAK;EAChC,MAAM,KAAK;EACX;EACA;EACA;EACA;CACD;CACD,MAAMC,eAA6B;EACjC,SAAS;EACT,WAAW,MAAM;EACjB,OAAO,MAAM;EACb,aAAa;EACb,YAAY,OAAO,QAAQ,IAAI,OAAK,EAAE,GAAG;EACzC,mBAAmB,OAAO,QAAQ,IAAI,OAAK,EAAE,GAAG;CACjD;CACD,MAAMC,gBAA+B;EACnC,SAAS;EACT,eAAe,MAAM;EACrB,iBAAiB,MAAM;EACvB,YAAY,MAAM;EAClB,eAAe,MAAM;EACrB,gBAAgB,MAAM;CACvB;CACD,MAAMC,eAA6B;EACjC,SAAS;EACT,MAAM,MAAM;EACZ,MAAM;GACJ,SAAS;GACT,KAAK,MAAM;EACZ;EACD,KAAK,MAAM;CAcZ;CACD,MAAM,aAAa,QAAQ,MAAM,SAAS,MAAM,UAAU;AAC1D,KAAI,YACF,aAAa,eAAe;CAE9B,MAAM,aAAa,QAAQ,MAAM,WAAW,MAAM,QAAQ,MAAM,WAAW,MAAM,gBAAgB,MAAM,YAAY;AACnH,KAAI,YACF,aAAa,UAAU;AAEzB,KAAI,MAAM,QACR,aAAa,SAAS,OAAO,OAAO,MAAM,OAAO;CAEnD,MAAMC,iBAAqC,SAAS,kBAAkB;EACpE,SAAS;EACT,MAAM;EACD;EACQ;EACb,WAAW;CACZ;CACD,MAAMC,YAA2B,SAAS,aAAa;EACrD,MAAM;EACC;EACM;EACR;EACL,UAAU,MAAM;EACT;EACP,QAAQ,OAAO;CAChB;CACD,MAAMC,cAA2C,SAAS,gBACxD,MAAM,cAAc;EAClB,MAAM;EACC;EACM;EACN;EACP,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,SAAS,MAAM;CAChB,IAAG;CAEN,MAAMC,QAAmB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD;AACD,4BACG,gBAAgB;EAAgB;EAC9B;GACwB;AAE9B"}
@@ -1,10 +1,10 @@
1
1
  import * as _websolutespa_bom_core0 from "@websolutespa/bom-core";
2
2
  import { ILocalized, IPage } from "@websolutespa/bom-core";
3
- import * as react11 from "react";
3
+ import * as react8 from "react";
4
4
 
5
5
  //#region src/client/page.d.ts
6
6
  type IPageContext = ILocalized<IPage>;
7
- declare const PageContext: react11.Context<{
7
+ declare const PageContext: react8.Context<{
8
8
  [x: string]: unknown;
9
9
  id: _websolutespa_bom_core0.IEquatable;
10
10
  schema: string;
@@ -324,6 +324,7 @@ declare const PageContext: react11.Context<{
324
324
  description?: string | undefined;
325
325
  keywords?: string | undefined;
326
326
  robots?: string | undefined;
327
+ robot?: string | undefined;
327
328
  image?: {
328
329
  [x: string]: unknown;
329
330
  id?: string | undefined;
@@ -397,7 +398,7 @@ declare function PageProvider({
397
398
  }: {
398
399
  page: ILocalized<IPage>;
399
400
  children?: React.ReactNode;
400
- }): react11.JSX.Element;
401
+ }): react8.JSX.Element;
401
402
  //# sourceMappingURL=page.d.ts.map
402
403
  //#endregion
403
404
  export { IPageContext, PageContext, PageProvider };
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","names":[],"sources":["../../src/client/page.tsx"],"sourcesContent":[],"mappings":";;;;;KAKY,YAAA,GAAe,WAAW;cAEzB,qBAAW;;MAAqD,uBAAA,CAAA;EAFjE,MAAA,EAAA,MAAA;EAAY,QAAA,EAAA;IAAc,UAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;IAAX,CAAA,CAAA,EAAA,MAAA,CAAA,EAAA,OAAA;IAAU,QAAA,EAAA,GAAA,GAAA,MAAA;IAExB,MAAA,EAAA,CAAA,GAAgE,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,UAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,MAAA,EAAA,CAAA,GAAA,OAAA,EAAA,MAAA,EAAA,EAAA,GAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAArD,GAAA,MAAA;IAAA,GAAA,EAAA,GAAA,GAAA,MAAA;IAER,QAAA,EAAA,CAAY,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,MAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAC1B,OAAA,EAAA,GAAA,GAAA,MAAA;IACA,SAAA,EAAA,GAAA,GAAA,MAAA;IAEiB,QAAA,EAAA,GAAA,GAAA,MAAA;IAAX,SAAA,EAAA,GAAA,GAAA,MAAA;IACK,QAAM,EAAA,CAAA,MAAA,QAAA,EAAA,uBAAA,gBAAA,CAAA;IAClB,UAAA,EAAA;MAAA,CAAA,WAAA,EAAA,MAAA,SAAA,EAAA,YAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBANe,YAAA;;;;QAIR,WAAW;aACN,KAAA,CAAM;IAClB,OAAA,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"page.d.ts","names":[],"sources":["../../src/client/page.tsx"],"sourcesContent":[],"mappings":";;;;;KAKY,YAAA,GAAe,WAAW;cAEzB,oBAAW;;MAAqD,uBAAA,CAAA;EAFjE,MAAA,EAAA,MAAA;EAAY,QAAA,EAAA;IAAc,UAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;IAAX,CAAA,CAAA,EAAA,MAAA,CAAA,EAAA,OAAA;IAAU,QAAA,EAAA,GAAA,GAAA,MAAA;IAExB,MAAA,EAAA,CAAA,GAAgE,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,UAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,MAAA,EAAA,CAAA,GAAA,OAAA,EAAA,MAAA,EAAA,EAAA,GAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAArD,GAAA,MAAA;IAAA,GAAA,EAAA,GAAA,GAAA,MAAA;IAER,QAAA,EAAA,CAAY,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAAA,MAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,UAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAA;IAC1B,OAAA,EAAA,GAAA,GAAA,MAAA;IACA,SAAA,EAAA,GAAA,GAAA,MAAA;IAEiB,QAAA,EAAA,GAAA,GAAA,MAAA;IAAX,SAAA,EAAA,GAAA,GAAA,MAAA;IACK,QAAM,EAAA,CAAA,MAAA,QAAA,EAAA,uBAAA,gBAAA,CAAA;IAClB,UAAA,EAAA;MAAA,CAAA,WAAA,EAAA,MAAA,SAAA,EAAA,YAAA,EAAA,MAAA,CAAA,EAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBANe,YAAA;;;;QAIR,WAAW;aACN,KAAA,CAAM;IAClB,MAAA,CAAA,GAAA,CAAA"}
@@ -1,13 +1,13 @@
1
1
  import { ApiRequestOptions } from "../lib/http.js";
2
- import * as react17 from "react";
2
+ import * as react15 from "react";
3
3
 
4
4
  //#region src/client/useApi.d.ts
5
- declare function useApiFetch<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
6
- declare function useApiGet<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
7
- declare function useApiPost<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
8
- declare function useApiPut<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
9
- declare function useApiPatch<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
10
- declare function useApiDelete<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react17.Dispatch<any>];
5
+ declare function useApiFetch<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
6
+ declare function useApiGet<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
7
+ declare function useApiPost<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
8
+ declare function useApiPut<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
9
+ declare function useApiPatch<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
10
+ declare function useApiDelete<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react15.Dispatch<any>];
11
11
  type Api = {
12
12
  get: <T = unknown>(pathname: string, options?: ApiRequestOptions) => Promise<T>;
13
13
  post: <T = unknown>(pathname: string, payload?: unknown, options?: ApiRequestOptions) => Promise<T>;
@@ -1,6 +1,6 @@
1
1
  import { ApiRequestOptions } from "../lib/http.js";
2
2
  import { QueryParams } from "@websolutespa/bom-core";
3
- import * as react0 from "react";
3
+ import * as react10 from "react";
4
4
 
5
5
  //#region src/client/useCollectionApi.d.ts
6
6
  type CollectionApi = {
@@ -11,11 +11,11 @@ type CollectionApi = {
11
11
  delete: <T = unknown>(pathname: string, options?: ApiRequestOptions) => Promise<T>;
12
12
  };
13
13
  declare function useCollectionApi(): CollectionApi;
14
- declare function useCollectionApiGet<T = unknown>(pathname: string, params?: QueryParams, options?: ApiRequestOptions): [T | undefined, boolean, any, react0.Dispatch<any>];
15
- declare function useCollectionApiPost<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react0.Dispatch<any>];
16
- declare function useCollectionApiPut<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react0.Dispatch<any>];
17
- declare function useCollectionApiPatch<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react0.Dispatch<any>];
18
- declare function useCollectionApiDelete<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react0.Dispatch<any>];
14
+ declare function useCollectionApiGet<T = unknown>(pathname: string, params?: QueryParams, options?: ApiRequestOptions): [T | undefined, boolean, any, react10.Dispatch<any>];
15
+ declare function useCollectionApiPost<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react10.Dispatch<any>];
16
+ declare function useCollectionApiPut<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react10.Dispatch<any>];
17
+ declare function useCollectionApiPatch<T = unknown>(pathname: string, payload: unknown, options?: ApiRequestOptions): [T | undefined, boolean, any, react10.Dispatch<any>];
18
+ declare function useCollectionApiDelete<T = unknown>(pathname: string, options?: ApiRequestOptions): [T | undefined, boolean, any, react10.Dispatch<any>];
19
19
  //#endregion
20
20
  export { useCollectionApi, useCollectionApiDelete, useCollectionApiGet, useCollectionApiPatch, useCollectionApiPost, useCollectionApiPut };
21
21
  //# sourceMappingURL=useCollectionApi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCollectionApi.d.ts","names":[],"sources":["../../src/client/useCollectionApi.ts"],"sourcesContent":[],"mappings":";;;;;KAMK,aAAA;gDAC2C,uBAAuB,sBAAsB,QAAQ;qEAChC,sBAAsB,QAAQ;EAF9F,GAAA,EAAA,CAAA,IAAA,OAAa,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAGkD,iBAHlD,EAAA,GAGwE,OAHxE,CAGgF,CAHhF,CAAA;EAAA,KAAA,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAIoD,iBAJpD,EAAA,GAI0E,OAJ1E,CAIkF,CAJlF,CAAA;EAAA,MAC8B,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAII,iBAJJ,EAAA,GAI0B,OAJ1B,CAIkC,CAJlC,CAAA;CAAW;AAA0C,iBAOrF,gBAAA,CAAA,CAPqF,EAOjE,aAPiE;AAAR,iBAuC7E,mBAvC6E,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAuCjB,WAvCiB,EAAA,OAAA,CAAA,EAuCK,iBAvCL,CAAA,EAAA,CAuC2B,CAvC3B,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAuC2B,MAAA,CAAA,QAvC3B,CAAA,GAAA,CAAA,CAAA;AACxB,iBA2CrD,oBA3CqD,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EA2C0B,iBA3C1B,CAAA,EAAA,CA2CgD,CA3ChD,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EA2CgD,MAAA,CAAA,QA3ChD,CAAA,GAAA,CAAA,CAAA;AAA8B,iBA+CnF,mBA/CmF,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EA+CL,iBA/CK,CAAA,EAAA,CA+CiB,CA/CjB,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EA+CiB,MAAA,CAAA,QA/CjB,CAAA,GAAA,CAAA,CAAA;AAAR,iBAmD3E,qBAnD2E,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAmDK,iBAnDL,CAAA,EAAA,CAmD2B,CAnD3B,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAmD2B,MAAA,CAAA,QAnD3B,CAAA,GAAA,CAAA,CAAA;AACvB,iBAsDpD,sBAtDoD,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAsDW,iBAtDX,CAAA,EAAA,CAsDiC,CAtDjC,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAsDiC,MAAA,CAAA,QAtDjC,CAAA,GAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"useCollectionApi.d.ts","names":[],"sources":["../../src/client/useCollectionApi.ts"],"sourcesContent":[],"mappings":";;;;;KAMK,aAAA;gDAC2C,uBAAuB,sBAAsB,QAAQ;qEAChC,sBAAsB,QAAQ;EAF9F,GAAA,EAAA,CAAA,IAAA,OAAa,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAGkD,iBAHlD,EAAA,GAGwE,OAHxE,CAGgF,CAHhF,CAAA;EAAA,KAAA,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAIoD,iBAJpD,EAAA,GAI0E,OAJ1E,CAIkF,CAJlF,CAAA;EAAA,MAC8B,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAII,iBAJJ,EAAA,GAI0B,OAJ1B,CAIkC,CAJlC,CAAA;CAAW;AAA0C,iBAOrF,gBAAA,CAAA,CAPqF,EAOjE,aAPiE;AAAR,iBAuC7E,mBAvC6E,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAuCjB,WAvCiB,EAAA,OAAA,CAAA,EAuCK,iBAvCL,CAAA,EAAA,CAuC2B,CAvC3B,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAuC2B,OAAA,CAAA,QAvC3B,CAAA,GAAA,CAAA,CAAA;AACxB,iBA2CrD,oBA3CqD,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EA2C0B,iBA3C1B,CAAA,EAAA,CA2CgD,CA3ChD,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EA2CgD,OAAA,CAAA,QA3ChD,CAAA,GAAA,CAAA,CAAA;AAA8B,iBA+CnF,mBA/CmF,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EA+CL,iBA/CK,CAAA,EAAA,CA+CiB,CA/CjB,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EA+CiB,OAAA,CAAA,QA/CjB,CAAA,GAAA,CAAA,CAAA;AAAR,iBAmD3E,qBAnD2E,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,OAAA,CAAA,EAmDK,iBAnDL,CAAA,EAAA,CAmD2B,CAnD3B,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAmD2B,OAAA,CAAA,QAnD3B,CAAA,GAAA,CAAA,CAAA;AACvB,iBAsDpD,sBAtDoD,CAAA,IAAA,OAAA,CAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAsDW,iBAtDX,CAAA,EAAA,CAsDiC,CAtDjC,GAAA,SAAA,EAAA,OAAA,EAAA,GAAA,EAsDiC,OAAA,CAAA,QAtDjC,CAAA,GAAA,CAAA,CAAA"}
@@ -321,6 +321,7 @@ declare const usePage: () => {
321
321
  description?: string | undefined;
322
322
  keywords?: string | undefined;
323
323
  robots?: string | undefined;
324
+ robot?: string | undefined;
324
325
  image?: {
325
326
  [x: string]: unknown;
326
327
  id?: string | undefined;
@@ -1,20 +1,27 @@
1
1
  import { IEndUser, IUser } from "../types.js";
2
- import * as react15 from "react";
2
+ import * as react21 from "react";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/client/user.d.ts
6
+ type UserValue = IEndUser | IUser | null;
6
7
  type UserContextType = {
7
- user?: IEndUser | IUser | null;
8
- setUser: (user?: IEndUser | IUser | null) => void;
8
+ user?: UserValue;
9
+ setUser: (user?: UserValue) => void;
9
10
  };
10
- declare const UserContext: react15.Context<UserContextType>;
11
+ declare const UserContext: react21.Context<UserContextType>;
12
+ /** Forces the next UserProvider mount to read `/auth/me` again. */
13
+ declare function invalidateUser(): void;
14
+ /**
15
+ * Provides the current user. Pass `initialUser` (a user or `null`) when the
16
+ * server already knows it, to skip the `/auth/me` request entirely.
17
+ */
11
18
  declare function UserProvider({
19
+ initialUser,
12
20
  children
13
21
  }: {
22
+ initialUser?: UserValue;
14
23
  children: ReactNode;
15
- }): react15.JSX.Element;
16
- //# sourceMappingURL=user.d.ts.map
17
-
24
+ }): react21.JSX.Element;
18
25
  //#endregion
19
- export { UserContext, UserContextType, UserProvider };
26
+ export { UserContext, UserContextType, UserProvider, invalidateUser };
20
27
  //# sourceMappingURL=user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","names":[],"sources":["../../src/client/user.tsx"],"sourcesContent":[],"mappings":";;;;;KAMY,eAAA;SACH,WAAW;mBACD,WAAW;AAF9B,CAAA;AAA2B,cAKd,WALc,EAKH,OAAA,CAAA,OALG,CAKH,eALG,CAAA;AAClB,iBAUO,YAAA,CAVP;EAAA;CAAA,EAAA;EAAQ,QAAG,EAaR,SAbQ;CAAK,CAAA,EAcxB,OAAA,CAAA,GAAA,CAAA,OAbkB"}
1
+ {"version":3,"file":"user.d.ts","names":[],"sources":["../../src/client/user.tsx"],"sourcesContent":[],"mappings":";;;;;KAMK,SAAA,GAAY,WAAW;KAEhB,eAAA;SACH;EAHJ,OAAA,EAAA,CAAA,IAAS,CAAA,EAIK,SAJL,EAAA,GAAA,IAAA;CAAA;AAAG,cAOJ,WAPI,EAOO,OAAA,CAAA,OAPP,CAOO,eAPP,CAAA;;AAAgB,iBA6BjB,cAAA,CAAA,CA7BiB,EAAA,IAAA;AAEjC;;;;AAE4B,iBAiCZ,YAAA,CAjCY;EAAA,WAAA;EAAA;CAAA,EAAA;EAGf,WAAA,CAAA,EAkCG,SA9Bd;EAAA,QAAA,EA+BU,SA/BV;CAAA,CAAA,EAgCD,OAAA,CAAA,GAAA,CAAA,OApCuB"}
@@ -2,28 +2,51 @@
2
2
 
3
3
 
4
4
  import { localApiGet } from "../lib/http.js";
5
- import { createContext, useEffect, useState } from "react";
5
+ import { createContext, useCallback, useEffect, useState } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/client/user.tsx
9
9
  const UserContext = createContext({ setUser: () => {
10
10
  console.log("not implemented");
11
11
  } });
12
- function UserProvider({ children }) {
13
- const [user, setUser] = useState();
12
+ let mePromise;
13
+ function getMe() {
14
+ if (!mePromise) mePromise = localApiGet("/auth/me").catch((error) => {
15
+ console.warn("UserProvider.getMe.error", error);
16
+ mePromise = void 0;
17
+ return null;
18
+ });
19
+ return mePromise;
20
+ }
21
+ /** Forces the next UserProvider mount to read `/auth/me` again. */
22
+ function invalidateUser() {
23
+ mePromise = void 0;
24
+ }
25
+ /**
26
+
27
+ * Provides the current user. Pass `initialUser` (a user or `null`) when the
28
+
29
+ * server already knows it, to skip the `/auth/me` request entirely.
30
+
31
+ */
32
+ function UserProvider({ initialUser, children }) {
33
+ const [user, setUserState] = useState(initialUser);
14
34
  useEffect(() => {
15
- const getMe = async () => {
16
- try {
17
- const user$1 = await localApiGet("/auth/me");
18
- return user$1;
19
- } catch (error) {
20
- console.log("UserProvider.getMe.error", error);
21
- return null;
22
- }
23
- };
35
+ if (initialUser !== void 0) {
36
+ mePromise = Promise.resolve(initialUser);
37
+ return;
38
+ }
39
+ let active = true;
24
40
  getMe().then((user$1) => {
25
- setUser(user$1);
41
+ if (active) setUserState(user$1);
26
42
  });
43
+ return () => {
44
+ active = false;
45
+ };
46
+ }, [initialUser]);
47
+ const setUser = useCallback((user$1) => {
48
+ mePromise = user$1 === void 0 ? void 0 : Promise.resolve(user$1);
49
+ setUserState(user$1);
27
50
  }, []);
28
51
  return /* @__PURE__ */ jsx(UserContext.Provider, {
29
52
  value: {
@@ -35,5 +58,5 @@ function UserProvider({ children }) {
35
58
  }
36
59
 
37
60
  //#endregion
38
- export { UserContext, UserProvider };
61
+ export { UserContext, UserProvider, invalidateUser };
39
62
  //# sourceMappingURL=user.js.map