dineway 0.1.3

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 (96) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +89 -0
  3. package/dist/adapters-BlzWJG82.d.mts +106 -0
  4. package/dist/apply-CAPvMfoU.mjs +1339 -0
  5. package/dist/astro/index.d.mts +50 -0
  6. package/dist/astro/index.mjs +1326 -0
  7. package/dist/astro/middleware/auth.d.mts +30 -0
  8. package/dist/astro/middleware/auth.mjs +708 -0
  9. package/dist/astro/middleware/redirect.d.mts +21 -0
  10. package/dist/astro/middleware/redirect.mjs +62 -0
  11. package/dist/astro/middleware/request-context.d.mts +17 -0
  12. package/dist/astro/middleware/request-context.mjs +1371 -0
  13. package/dist/astro/middleware/setup.d.mts +19 -0
  14. package/dist/astro/middleware/setup.mjs +46 -0
  15. package/dist/astro/middleware.d.mts +12 -0
  16. package/dist/astro/middleware.mjs +1716 -0
  17. package/dist/astro/types.d.mts +269 -0
  18. package/dist/astro/types.mjs +1 -0
  19. package/dist/base64-F8-DUraK.mjs +58 -0
  20. package/dist/byline-DeWCMU_i.mjs +234 -0
  21. package/dist/bylines-DyqBV9EQ.mjs +137 -0
  22. package/dist/chunk-ClPoSABd.mjs +21 -0
  23. package/dist/cli/index.d.mts +1 -0
  24. package/dist/cli/index.mjs +3987 -0
  25. package/dist/client/external-auth-headers.d.mts +38 -0
  26. package/dist/client/external-auth-headers.mjs +101 -0
  27. package/dist/client/index.d.mts +397 -0
  28. package/dist/client/index.mjs +345 -0
  29. package/dist/config-Cq8H0SfX.mjs +46 -0
  30. package/dist/connection-C9pxzuag.mjs +52 -0
  31. package/dist/content-zSgdNmnt.mjs +836 -0
  32. package/dist/db/index.d.mts +4 -0
  33. package/dist/db/index.mjs +62 -0
  34. package/dist/db/libsql.d.mts +10 -0
  35. package/dist/db/libsql.mjs +21 -0
  36. package/dist/db/postgres.d.mts +10 -0
  37. package/dist/db/postgres.mjs +29 -0
  38. package/dist/db/sqlite.d.mts +10 -0
  39. package/dist/db/sqlite.mjs +15 -0
  40. package/dist/default-WYlzADZL.mjs +80 -0
  41. package/dist/dialect-helpers-B9uSp2GJ.mjs +89 -0
  42. package/dist/error-DrxtnGPg.mjs +26 -0
  43. package/dist/index-C-jx21qs.d.mts +4771 -0
  44. package/dist/index.d.mts +16 -0
  45. package/dist/index.mjs +30 -0
  46. package/dist/load-C6FCD1FU.mjs +27 -0
  47. package/dist/loader-qKmo0wAY.mjs +446 -0
  48. package/dist/manifest-schema-CTSEyIJ3.mjs +186 -0
  49. package/dist/media/index.d.mts +25 -0
  50. package/dist/media/index.mjs +54 -0
  51. package/dist/media/local-runtime.d.mts +38 -0
  52. package/dist/media/local-runtime.mjs +132 -0
  53. package/dist/media-DMTr80Gv.mjs +199 -0
  54. package/dist/mode-BlyYtIFO.mjs +22 -0
  55. package/dist/page/index.d.mts +148 -0
  56. package/dist/page/index.mjs +419 -0
  57. package/dist/placeholder-B3knXwNc.mjs +267 -0
  58. package/dist/placeholder-bOx1xCTY.d.mts +283 -0
  59. package/dist/plugin-utils.d.mts +57 -0
  60. package/dist/plugin-utils.mjs +77 -0
  61. package/dist/plugins/adapt-sandbox-entry.d.mts +21 -0
  62. package/dist/plugins/adapt-sandbox-entry.mjs +112 -0
  63. package/dist/query-BiaPl_g2.mjs +459 -0
  64. package/dist/redirect-JPqLAbxa.mjs +328 -0
  65. package/dist/registry-DSd1GWB8.mjs +851 -0
  66. package/dist/request-context.d.mts +49 -0
  67. package/dist/request-context.mjs +42 -0
  68. package/dist/runner-B5l1JfOj.d.mts +26 -0
  69. package/dist/runner-BGUGywgG.mjs +1529 -0
  70. package/dist/runtime.d.mts +25 -0
  71. package/dist/runtime.mjs +41 -0
  72. package/dist/search-BNruJHDL.mjs +11054 -0
  73. package/dist/seed/index.d.mts +3 -0
  74. package/dist/seed/index.mjs +15 -0
  75. package/dist/seo/index.d.mts +69 -0
  76. package/dist/seo/index.mjs +69 -0
  77. package/dist/storage/local.d.mts +38 -0
  78. package/dist/storage/local.mjs +165 -0
  79. package/dist/storage/s3.d.mts +31 -0
  80. package/dist/storage/s3.mjs +174 -0
  81. package/dist/tokens-4vgYuXsZ.mjs +170 -0
  82. package/dist/transport-C5FYnid7.mjs +417 -0
  83. package/dist/transport-gIL-e43D.d.mts +41 -0
  84. package/dist/types-BawVha09.mjs +30 -0
  85. package/dist/types-BgQeVaPj.d.mts +192 -0
  86. package/dist/types-CLLdsG3g.d.mts +103 -0
  87. package/dist/types-D38djUXv.d.mts +1196 -0
  88. package/dist/types-DShnjzb6.mjs +15 -0
  89. package/dist/types-DkvMXalq.d.mts +425 -0
  90. package/dist/types-DuNbGKjF.mjs +74 -0
  91. package/dist/types-ju-_ORz7.d.mts +182 -0
  92. package/dist/validate-CXnRKfJK.mjs +327 -0
  93. package/dist/validate-CqRJb_xU.mjs +96 -0
  94. package/dist/validate-DVKJJ-M_.d.mts +377 -0
  95. package/locals.d.ts +47 -0
  96. package/package.json +313 -0
@@ -0,0 +1,148 @@
1
+ import { G as PublicPageContext, M as PagePlacement, O as PageMetadataContribution, T as PageFragmentContribution, j as PageMetadataLinkRel, t as BreadcrumbItem } from "../types-D38djUXv.mjs";
2
+
3
+ //#region src/page/context.d.ts
4
+ /** Fields shared by both input forms */
5
+ interface PageContextFields {
6
+ kind: "content" | "custom";
7
+ pageType?: string;
8
+ title?: string | null;
9
+ pageTitle?: string | null;
10
+ description?: string | null;
11
+ canonical?: string | null;
12
+ image?: string | null;
13
+ content?: {
14
+ collection: string;
15
+ id: string;
16
+ slug?: string | null;
17
+ };
18
+ /** SEO overrides for OG/Twitter meta generation */
19
+ seo?: {
20
+ ogTitle?: string | null;
21
+ ogDescription?: string | null;
22
+ ogImage?: string | null;
23
+ robots?: string | null;
24
+ };
25
+ /** Article metadata for Open Graph article: tags */
26
+ articleMeta?: {
27
+ publishedTime?: string | null;
28
+ modifiedTime?: string | null;
29
+ author?: string | null;
30
+ };
31
+ /** Site name for structured data and og:site_name */
32
+ siteName?: string;
33
+ /**
34
+ * Breadcrumb trail for this page, root first. Pass an empty array
35
+ * to explicitly opt out of breadcrumbs (e.g. homepage), or omit the
36
+ * field to let consumers fall back to their own derivation.
37
+ */
38
+ breadcrumbs?: BreadcrumbItem[];
39
+ /** Public-facing site URL (origin) for structured data */
40
+ siteUrl?: string;
41
+ }
42
+ /** Input with Astro global -- used in .astro files */
43
+ interface AstroInput extends PageContextFields {
44
+ Astro: {
45
+ url: URL;
46
+ currentLocale?: string;
47
+ };
48
+ }
49
+ /** Input with explicit URL -- used outside .astro files */
50
+ interface UrlInput extends PageContextFields {
51
+ url: URL | string;
52
+ locale?: string;
53
+ }
54
+ type CreatePublicPageContextInput = AstroInput | UrlInput;
55
+ /**
56
+ * Build a PublicPageContext from template input.
57
+ */
58
+ declare function createPublicPageContext(input: CreatePublicPageContextInput): PublicPageContext;
59
+ //#endregion
60
+ //#region src/page/metadata.d.ts
61
+ interface ResolvedPageMetadata {
62
+ meta: Array<{
63
+ name: string;
64
+ content: string;
65
+ }>;
66
+ properties: Array<{
67
+ property: string;
68
+ content: string;
69
+ }>;
70
+ links: Array<{
71
+ rel: PageMetadataLinkRel;
72
+ href: string;
73
+ hreflang?: string;
74
+ }>;
75
+ jsonld: Array<{
76
+ id?: string;
77
+ json: string;
78
+ }>;
79
+ }
80
+ /** Escape a string for safe use in an HTML attribute value */
81
+ declare function escapeHtmlAttr(value: string): string;
82
+ /**
83
+ * Safely serialize a value for embedding in a <script type="application/ld+json"> tag.
84
+ *
85
+ * Plain JSON.stringify is not sufficient because:
86
+ * - "</script>" in a nested string breaks out of the script tag
87
+ * - "<!--" can open an HTML comment
88
+ * - U+2028/U+2029 are line terminators in some JS engines
89
+ */
90
+ declare function safeJsonLdSerialize(value: unknown): string;
91
+ /**
92
+ * Resolve a flat list of contributions into deduplicated metadata.
93
+ * First contribution wins for any given dedupe key.
94
+ */
95
+ declare function resolvePageMetadata(contributions: PageMetadataContribution[]): ResolvedPageMetadata;
96
+ /** Render resolved metadata to an HTML string for embedding in <head> */
97
+ declare function renderPageMetadata(metadata: ResolvedPageMetadata): string;
98
+ //#endregion
99
+ //#region src/page/fragments.d.ts
100
+ /**
101
+ * Filter contributions to a specific placement and deduplicate.
102
+ * - Contributions with the same `key + placement` are deduped (first wins).
103
+ * - External scripts with the same `src + placement` are deduped.
104
+ */
105
+ declare function resolveFragments(contributions: PageFragmentContribution[], placement: PagePlacement): PageFragmentContribution[];
106
+ /** Render a list of fragment contributions to an HTML string */
107
+ declare function renderFragments(contributions: PageFragmentContribution[], placement: PagePlacement): string;
108
+ //#endregion
109
+ //#region src/page/seo-contributions.d.ts
110
+ /**
111
+ * Generate base metadata contributions from a page context's SEO data.
112
+ * Returns an empty array if no SEO-relevant data is present.
113
+ */
114
+ declare function generateBaseSeoContributions(page: PublicPageContext): PageMetadataContribution[];
115
+ //#endregion
116
+ //#region src/page/jsonld.d.ts
117
+ /**
118
+ * Remove null/undefined values from a JSON-LD object recursively.
119
+ * JSON-LD validators prefer absent keys over null values.
120
+ */
121
+ declare function cleanJsonLd(obj: Record<string, unknown>): Record<string, unknown>;
122
+ /**
123
+ * Build a BlogPosting JSON-LD graph from page context.
124
+ * Used for article-type content pages.
125
+ */
126
+ declare function buildBlogPostingJsonLd(page: PublicPageContext): Record<string, unknown> | null;
127
+ /**
128
+ * Build a WebSite JSON-LD graph from page context.
129
+ * Used for non-article pages (homepage, listing pages, etc.)
130
+ */
131
+ declare function buildWebSiteJsonLd(page: PublicPageContext): Record<string, unknown> | null;
132
+ //#endregion
133
+ //#region src/page/index.d.ts
134
+ /**
135
+ * Shape of the Dineway runtime methods used by the render components.
136
+ * Extracted here so all three components share a single type definition.
137
+ */
138
+ interface DinewayPageRuntime {
139
+ collectPageMetadata: (page: PublicPageContext) => Promise<PageMetadataContribution[]>;
140
+ collectPageFragments: (page: PublicPageContext) => Promise<PageFragmentContribution[]>;
141
+ }
142
+ /**
143
+ * Get the page runtime from Astro locals. Returns undefined when
144
+ * Dineway is not initialized (components render nothing in that case).
145
+ */
146
+ declare function getPageRuntime(locals: Record<string, unknown>): DinewayPageRuntime | undefined;
147
+ //#endregion
148
+ export { type CreatePublicPageContextInput, DinewayPageRuntime, type ResolvedPageMetadata, buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };
@@ -0,0 +1,419 @@
1
+ //#region src/page/context.ts
2
+ function isAstroInput(input) {
3
+ return "Astro" in input;
4
+ }
5
+ /**
6
+ * Build a PublicPageContext from template input.
7
+ */
8
+ function createPublicPageContext(input) {
9
+ let url;
10
+ let path;
11
+ let locale;
12
+ if (isAstroInput(input)) {
13
+ url = input.Astro.url.href;
14
+ path = input.Astro.url.pathname;
15
+ locale = input.Astro.currentLocale ?? null;
16
+ } else {
17
+ const parsed = typeof input.url === "string" ? new URL(input.url) : input.url;
18
+ url = parsed.href;
19
+ path = parsed.pathname;
20
+ locale = input.locale ?? null;
21
+ }
22
+ return {
23
+ url,
24
+ path,
25
+ locale,
26
+ kind: input.kind,
27
+ pageType: input.pageType ?? (input.kind === "content" ? "article" : "website"),
28
+ title: input.title ?? null,
29
+ pageTitle: input.pageTitle ?? null,
30
+ description: input.description ?? null,
31
+ canonical: input.canonical ?? null,
32
+ image: input.image ?? null,
33
+ content: input.content ? {
34
+ collection: input.content.collection,
35
+ id: input.content.id,
36
+ slug: input.content.slug ?? null
37
+ } : void 0,
38
+ seo: input.seo,
39
+ articleMeta: input.articleMeta,
40
+ siteName: input.siteName,
41
+ breadcrumbs: input.breadcrumbs,
42
+ siteUrl: input.siteUrl
43
+ };
44
+ }
45
+
46
+ //#endregion
47
+ //#region src/page/metadata.ts
48
+ /** Schemes safe for use in link href attributes */
49
+ const SAFE_HREF_RE = /^(https?|at):\/\//i;
50
+ const HTML_ESCAPE_MAP = {
51
+ "&": "&amp;",
52
+ "<": "&lt;",
53
+ ">": "&gt;",
54
+ "\"": "&quot;",
55
+ "'": "&#39;"
56
+ };
57
+ const HTML_ESCAPE_RE = /[&<>"']/g;
58
+ /** Escape a string for safe use in an HTML attribute value */
59
+ function escapeHtmlAttr(value) {
60
+ return value.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch] ?? ch);
61
+ }
62
+ /** Validate that a URL uses a safe scheme (http, https, at) */
63
+ function isSafeHref(url) {
64
+ return SAFE_HREF_RE.test(url);
65
+ }
66
+ const JSONLD_LT_RE = /</g;
67
+ const JSONLD_GT_RE = />/g;
68
+ const JSONLD_U2028_RE = /\u2028/g;
69
+ const JSONLD_U2029_RE = /\u2029/g;
70
+ /**
71
+ * Safely serialize a value for embedding in a <script type="application/ld+json"> tag.
72
+ *
73
+ * Plain JSON.stringify is not sufficient because:
74
+ * - "<\/script>" in a nested string breaks out of the script tag
75
+ * - "<!--" can open an HTML comment
76
+ * - U+2028/U+2029 are line terminators in some JS engines
77
+ */
78
+ function safeJsonLdSerialize(value) {
79
+ return JSON.stringify(value).replace(JSONLD_LT_RE, "\\u003c").replace(JSONLD_GT_RE, "\\u003e").replace(JSONLD_U2028_RE, "\\u2028").replace(JSONLD_U2029_RE, "\\u2029");
80
+ }
81
+ /**
82
+ * Resolve a flat list of contributions into deduplicated metadata.
83
+ * First contribution wins for any given dedupe key.
84
+ */
85
+ function resolvePageMetadata(contributions) {
86
+ const result = {
87
+ meta: [],
88
+ properties: [],
89
+ links: [],
90
+ jsonld: []
91
+ };
92
+ const seenMeta = /* @__PURE__ */ new Set();
93
+ const seenProperties = /* @__PURE__ */ new Set();
94
+ const seenLinks = /* @__PURE__ */ new Set();
95
+ const seenJsonLd = /* @__PURE__ */ new Set();
96
+ for (const c of contributions) switch (c.kind) {
97
+ case "meta": {
98
+ const dedupeKey = c.key ?? c.name;
99
+ if (seenMeta.has(dedupeKey)) continue;
100
+ seenMeta.add(dedupeKey);
101
+ result.meta.push({
102
+ name: c.name,
103
+ content: c.content
104
+ });
105
+ break;
106
+ }
107
+ case "property": {
108
+ const dedupeKey = c.key ?? c.property;
109
+ if (seenProperties.has(dedupeKey)) continue;
110
+ seenProperties.add(dedupeKey);
111
+ result.properties.push({
112
+ property: c.property,
113
+ content: c.content
114
+ });
115
+ break;
116
+ }
117
+ case "link":
118
+ if (!isSafeHref(c.href)) {
119
+ if (import.meta.env?.DEV) console.warn(`[page:metadata] Rejected link contribution with unsafe href scheme: ${c.href}`);
120
+ continue;
121
+ }
122
+ if (c.rel === "canonical") {
123
+ if (seenLinks.has("canonical")) continue;
124
+ seenLinks.add("canonical");
125
+ } else {
126
+ const dedupeKey = c.key ?? c.hreflang ?? c.href;
127
+ if (seenLinks.has(dedupeKey)) continue;
128
+ seenLinks.add(dedupeKey);
129
+ }
130
+ result.links.push({
131
+ rel: c.rel,
132
+ href: c.href,
133
+ ...c.hreflang && { hreflang: c.hreflang }
134
+ });
135
+ break;
136
+ case "jsonld":
137
+ if (c.id) {
138
+ if (seenJsonLd.has(c.id)) continue;
139
+ seenJsonLd.add(c.id);
140
+ }
141
+ result.jsonld.push({
142
+ id: c.id,
143
+ json: safeJsonLdSerialize(c.graph)
144
+ });
145
+ break;
146
+ default: break;
147
+ }
148
+ return result;
149
+ }
150
+ /** Render resolved metadata to an HTML string for embedding in <head> */
151
+ function renderPageMetadata(metadata) {
152
+ const parts = [];
153
+ for (const m of metadata.meta) parts.push(`<meta name="${escapeHtmlAttr(m.name)}" content="${escapeHtmlAttr(m.content)}">`);
154
+ for (const p of metadata.properties) parts.push(`<meta property="${escapeHtmlAttr(p.property)}" content="${escapeHtmlAttr(p.content)}">`);
155
+ for (const l of metadata.links) {
156
+ let tag = `<link rel="${escapeHtmlAttr(l.rel)}" href="${escapeHtmlAttr(l.href)}"`;
157
+ if (l.hreflang) tag += ` hreflang="${escapeHtmlAttr(l.hreflang)}"`;
158
+ tag += ">";
159
+ parts.push(tag);
160
+ }
161
+ for (const j of metadata.jsonld) parts.push(`<script type="application/ld+json">${j.json}<\/script>`);
162
+ return parts.join("\n");
163
+ }
164
+
165
+ //#endregion
166
+ //#region src/page/fragments.ts
167
+ /** Escape sequences that would break out of a script tag */
168
+ const SCRIPT_CLOSE_RE = /<\//g;
169
+ /**
170
+ * Filter contributions to a specific placement and deduplicate.
171
+ * - Contributions with the same `key + placement` are deduped (first wins).
172
+ * - External scripts with the same `src + placement` are deduped.
173
+ */
174
+ function resolveFragments(contributions, placement) {
175
+ const filtered = contributions.filter((c) => c.placement === placement);
176
+ const seen = /* @__PURE__ */ new Set();
177
+ const result = [];
178
+ for (const c of filtered) {
179
+ if (c.key) {
180
+ const dedupeKey = `key:${c.key}`;
181
+ if (seen.has(dedupeKey)) continue;
182
+ seen.add(dedupeKey);
183
+ } else if (c.kind === "external-script") {
184
+ const dedupeKey = `src:${c.src}`;
185
+ if (seen.has(dedupeKey)) continue;
186
+ seen.add(dedupeKey);
187
+ }
188
+ result.push(c);
189
+ }
190
+ return result;
191
+ }
192
+ const EVENT_HANDLER_RE = /^on/i;
193
+ function renderAttributes(attrs) {
194
+ return Object.entries(attrs).filter(([k]) => !EVENT_HANDLER_RE.test(k)).map(([k, v]) => ` ${escapeHtmlAttr(k)}="${escapeHtmlAttr(v)}"`).join("");
195
+ }
196
+ /** Render a single fragment contribution to HTML */
197
+ function renderFragment(c) {
198
+ switch (c.kind) {
199
+ case "external-script": {
200
+ let tag = `<script src="${escapeHtmlAttr(c.src)}"`;
201
+ if (c.async) tag += " async";
202
+ if (c.defer) tag += " defer";
203
+ if (c.attributes) tag += renderAttributes(c.attributes);
204
+ tag += "><\/script>";
205
+ return tag;
206
+ }
207
+ case "inline-script": {
208
+ let tag = "<script";
209
+ if (c.attributes) tag += renderAttributes(c.attributes);
210
+ tag += `>${c.code.replace(SCRIPT_CLOSE_RE, "<\\/")}<\/script>`;
211
+ return tag;
212
+ }
213
+ case "html": return c.html;
214
+ }
215
+ }
216
+ /** Render a list of fragment contributions to an HTML string */
217
+ function renderFragments(contributions, placement) {
218
+ return resolveFragments(contributions, placement).map(renderFragment).join("\n");
219
+ }
220
+
221
+ //#endregion
222
+ //#region src/page/jsonld.ts
223
+ /**
224
+ * Remove null/undefined values from a JSON-LD object recursively.
225
+ * JSON-LD validators prefer absent keys over null values.
226
+ */
227
+ function cleanJsonLd(obj) {
228
+ const cleaned = {};
229
+ for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== null) if (typeof value === "object" && !Array.isArray(value)) cleaned[key] = cleanJsonLd(value);
230
+ else cleaned[key] = value;
231
+ return cleaned;
232
+ }
233
+ /**
234
+ * Build a BlogPosting JSON-LD graph from page context.
235
+ * Used for article-type content pages.
236
+ */
237
+ function buildBlogPostingJsonLd(page) {
238
+ if (page.pageType !== "article" || !page.canonical) return null;
239
+ const ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;
240
+ const description = page.seo?.ogDescription || page.description;
241
+ const ogImage = page.seo?.ogImage || page.image;
242
+ const publishedTime = page.articleMeta?.publishedTime;
243
+ const modifiedTime = page.articleMeta?.modifiedTime;
244
+ const author = page.articleMeta?.author;
245
+ const siteName = page.siteName;
246
+ return cleanJsonLd({
247
+ "@context": "https://schema.org",
248
+ "@type": "BlogPosting",
249
+ headline: ogTitle,
250
+ description,
251
+ image: ogImage || void 0,
252
+ url: page.canonical,
253
+ datePublished: publishedTime || void 0,
254
+ dateModified: modifiedTime || publishedTime || void 0,
255
+ author: author ? {
256
+ "@type": "Person",
257
+ name: author
258
+ } : void 0,
259
+ publisher: siteName ? {
260
+ "@type": "Organization",
261
+ name: siteName
262
+ } : void 0,
263
+ mainEntityOfPage: {
264
+ "@type": "WebPage",
265
+ "@id": page.canonical
266
+ }
267
+ });
268
+ }
269
+ /**
270
+ * Build a WebSite JSON-LD graph from page context.
271
+ * Used for non-article pages (homepage, listing pages, etc.)
272
+ */
273
+ function buildWebSiteJsonLd(page) {
274
+ const siteName = page.siteName;
275
+ if (!siteName) return null;
276
+ let siteUrl;
277
+ if (page.siteUrl) siteUrl = page.siteUrl;
278
+ else try {
279
+ siteUrl = new URL(page.url).origin;
280
+ } catch {
281
+ siteUrl = page.canonical || page.url;
282
+ }
283
+ return cleanJsonLd({
284
+ "@context": "https://schema.org",
285
+ "@type": "WebSite",
286
+ name: siteName,
287
+ url: siteUrl
288
+ });
289
+ }
290
+
291
+ //#endregion
292
+ //#region src/page/seo-contributions.ts
293
+ /**
294
+ * Generate base metadata contributions from a page context's SEO data.
295
+ * Returns an empty array if no SEO-relevant data is present.
296
+ */
297
+ function generateBaseSeoContributions(page) {
298
+ const contributions = [];
299
+ const description = page.description;
300
+ const ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;
301
+ const ogDescription = page.seo?.ogDescription || description;
302
+ const ogImage = page.seo?.ogImage || page.image;
303
+ const robots = page.seo?.robots;
304
+ const canonical = page.canonical;
305
+ const siteName = page.siteName;
306
+ if (description) contributions.push({
307
+ kind: "meta",
308
+ name: "description",
309
+ content: description
310
+ });
311
+ if (robots) contributions.push({
312
+ kind: "meta",
313
+ name: "robots",
314
+ content: robots
315
+ });
316
+ if (canonical) contributions.push({
317
+ kind: "link",
318
+ rel: "canonical",
319
+ href: canonical
320
+ });
321
+ contributions.push({
322
+ kind: "property",
323
+ property: "og:type",
324
+ content: page.pageType === "article" ? "article" : "website"
325
+ });
326
+ if (ogTitle) contributions.push({
327
+ kind: "property",
328
+ property: "og:title",
329
+ content: ogTitle
330
+ });
331
+ if (ogDescription) contributions.push({
332
+ kind: "property",
333
+ property: "og:description",
334
+ content: ogDescription
335
+ });
336
+ if (ogImage) contributions.push({
337
+ kind: "property",
338
+ property: "og:image",
339
+ content: ogImage
340
+ });
341
+ if (canonical) contributions.push({
342
+ kind: "property",
343
+ property: "og:url",
344
+ content: canonical
345
+ });
346
+ if (siteName) contributions.push({
347
+ kind: "property",
348
+ property: "og:site_name",
349
+ content: siteName
350
+ });
351
+ contributions.push({
352
+ kind: "meta",
353
+ name: "twitter:card",
354
+ content: ogImage ? "summary_large_image" : "summary"
355
+ });
356
+ if (ogTitle) contributions.push({
357
+ kind: "meta",
358
+ name: "twitter:title",
359
+ content: ogTitle
360
+ });
361
+ if (ogDescription) contributions.push({
362
+ kind: "meta",
363
+ name: "twitter:description",
364
+ content: ogDescription
365
+ });
366
+ if (ogImage) contributions.push({
367
+ kind: "meta",
368
+ name: "twitter:image",
369
+ content: ogImage
370
+ });
371
+ if (page.pageType === "article" && page.articleMeta) {
372
+ const { publishedTime, modifiedTime, author } = page.articleMeta;
373
+ if (publishedTime) contributions.push({
374
+ kind: "property",
375
+ property: "article:published_time",
376
+ content: publishedTime
377
+ });
378
+ if (modifiedTime) contributions.push({
379
+ kind: "property",
380
+ property: "article:modified_time",
381
+ content: modifiedTime
382
+ });
383
+ if (author) contributions.push({
384
+ kind: "property",
385
+ property: "article:author",
386
+ content: author
387
+ });
388
+ }
389
+ if (page.pageType === "article") {
390
+ const blogPosting = buildBlogPostingJsonLd(page);
391
+ if (blogPosting) contributions.push({
392
+ kind: "jsonld",
393
+ id: "primary",
394
+ graph: blogPosting
395
+ });
396
+ } else if (siteName) {
397
+ const webSite = buildWebSiteJsonLd(page);
398
+ if (webSite) contributions.push({
399
+ kind: "jsonld",
400
+ id: "primary",
401
+ graph: webSite
402
+ });
403
+ }
404
+ return contributions;
405
+ }
406
+
407
+ //#endregion
408
+ //#region src/page/index.ts
409
+ /**
410
+ * Get the page runtime from Astro locals. Returns undefined when
411
+ * Dineway is not initialized (components render nothing in that case).
412
+ */
413
+ function getPageRuntime(locals) {
414
+ const dineway = locals.dineway;
415
+ if (dineway && typeof dineway === "object" && "collectPageMetadata" in dineway && "collectPageFragments" in dineway) return dineway;
416
+ }
417
+
418
+ //#endregion
419
+ export { buildBlogPostingJsonLd, buildWebSiteJsonLd, cleanJsonLd, createPublicPageContext, escapeHtmlAttr, generateBaseSeoContributions, getPageRuntime, renderFragments, renderPageMetadata, resolveFragments, resolvePageMetadata, safeJsonLdSerialize };