@sonordev/site-kit 2.2.9 → 2.5.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.
Files changed (112) hide show
  1. package/dist/blog/index.d.mts +31 -3
  2. package/dist/blog/index.d.ts +31 -3
  3. package/dist/blog/index.js +194 -10
  4. package/dist/blog/index.js.map +1 -1
  5. package/dist/blog/index.mjs +183 -4
  6. package/dist/blog/index.mjs.map +1 -1
  7. package/dist/blog/server-ui.d.mts +1 -1
  8. package/dist/blog/server-ui.d.ts +1 -1
  9. package/dist/blog/server-ui.js +3 -3
  10. package/dist/blog/server-ui.mjs +1 -1
  11. package/dist/blog/server.d.mts +79 -7
  12. package/dist/blog/server.d.ts +79 -7
  13. package/dist/blog/server.js +64 -32
  14. package/dist/blog/server.mjs +1 -1
  15. package/dist/{chunk-WRCX2NKY.mjs → chunk-2NM6RGAV.mjs} +226 -22
  16. package/dist/chunk-2NM6RGAV.mjs.map +1 -0
  17. package/dist/chunk-5B4FABFK.js +28 -0
  18. package/dist/chunk-5B4FABFK.js.map +1 -0
  19. package/dist/{chunk-DTVZJPVM.mjs → chunk-5SQ4NRPH.mjs} +9 -2
  20. package/dist/chunk-5SQ4NRPH.mjs.map +1 -0
  21. package/dist/chunk-ATG4FJY6.js +76 -0
  22. package/dist/chunk-ATG4FJY6.js.map +1 -0
  23. package/dist/{chunk-GQKBGL2W.js → chunk-DZKX3GHL.js} +233 -21
  24. package/dist/chunk-DZKX3GHL.js.map +1 -0
  25. package/dist/{chunk-LNMI6OMN.js → chunk-F54HGPDM.js} +137 -4
  26. package/dist/chunk-F54HGPDM.js.map +1 -0
  27. package/dist/chunk-H23ZT2I2.mjs +67 -0
  28. package/dist/chunk-H23ZT2I2.mjs.map +1 -0
  29. package/dist/chunk-H4OBGC43.mjs +26 -0
  30. package/dist/chunk-H4OBGC43.mjs.map +1 -0
  31. package/dist/{chunk-Z6EHHJWU.mjs → chunk-MNOVPHL6.mjs} +230 -35
  32. package/dist/chunk-MNOVPHL6.mjs.map +1 -0
  33. package/dist/{chunk-ITPVKQB6.js → chunk-MWE2HRPU.js} +229 -34
  34. package/dist/chunk-MWE2HRPU.js.map +1 -0
  35. package/dist/{chunk-AWMEH65F.js → chunk-PAF5IGGF.js} +9 -2
  36. package/dist/chunk-PAF5IGGF.js.map +1 -0
  37. package/dist/{chunk-OOZCN7AF.mjs → chunk-T5UU7I4V.mjs} +137 -5
  38. package/dist/chunk-T5UU7I4V.mjs.map +1 -0
  39. package/dist/cli/index.js +352 -78
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/index.mjs +352 -78
  42. package/dist/cli/index.mjs.map +1 -1
  43. package/dist/config/index.d.mts +17 -0
  44. package/dist/config/index.d.ts +17 -0
  45. package/dist/config/index.js +43 -3
  46. package/dist/config/index.js.map +1 -1
  47. package/dist/config/index.mjs +43 -3
  48. package/dist/config/index.mjs.map +1 -1
  49. package/dist/forms/index.js +3 -1
  50. package/dist/forms/index.js.map +1 -1
  51. package/dist/forms/index.mjs +3 -1
  52. package/dist/forms/index.mjs.map +1 -1
  53. package/dist/index.d.mts +2 -2
  54. package/dist/index.d.ts +2 -2
  55. package/dist/layout/index.d.mts +6 -1
  56. package/dist/layout/index.d.ts +6 -1
  57. package/dist/layout/index.js +7 -3
  58. package/dist/layout/index.js.map +1 -1
  59. package/dist/layout/index.mjs +7 -3
  60. package/dist/layout/index.mjs.map +1 -1
  61. package/dist/llms/contract.d.mts +43 -0
  62. package/dist/llms/contract.d.ts +43 -0
  63. package/dist/llms/contract.js +41 -0
  64. package/dist/llms/contract.js.map +1 -0
  65. package/dist/llms/contract.mjs +4 -0
  66. package/dist/llms/contract.mjs.map +1 -0
  67. package/dist/llms/index.d.mts +67 -5
  68. package/dist/llms/index.d.ts +67 -5
  69. package/dist/llms/index.js +154 -36
  70. package/dist/llms/index.js.map +1 -1
  71. package/dist/llms/index.mjs +107 -27
  72. package/dist/llms/index.mjs.map +1 -1
  73. package/dist/middleware/index.d.mts +13 -1
  74. package/dist/middleware/index.d.ts +13 -1
  75. package/dist/middleware/index.js +11 -0
  76. package/dist/middleware/index.js.map +1 -1
  77. package/dist/middleware/index.mjs +11 -0
  78. package/dist/middleware/index.mjs.map +1 -1
  79. package/dist/{routing-ccNYbFLU.d.ts → routing-C7gmHWm9.d.ts} +1 -1
  80. package/dist/{routing-ebQln7wH.d.mts → routing-trNzR1Pz.d.mts} +1 -1
  81. package/dist/seo/index.d.mts +19 -4
  82. package/dist/seo/index.d.ts +19 -4
  83. package/dist/seo/index.js +49 -14
  84. package/dist/seo/index.js.map +1 -1
  85. package/dist/seo/index.mjs +42 -8
  86. package/dist/seo/index.mjs.map +1 -1
  87. package/dist/seo/server.d.mts +2 -2
  88. package/dist/seo/server.d.ts +2 -2
  89. package/dist/seo/server.js +5 -5
  90. package/dist/seo/server.mjs +1 -1
  91. package/dist/sitemap/index.d.mts +8 -1
  92. package/dist/sitemap/index.d.ts +8 -1
  93. package/dist/sitemap/index.js +24 -4
  94. package/dist/sitemap/index.js.map +1 -1
  95. package/dist/sitemap/index.mjs +23 -3
  96. package/dist/sitemap/index.mjs.map +1 -1
  97. package/dist/{types-BxzT7yhf.d.mts → types-0NuBL1Gg.d.ts} +34 -0
  98. package/dist/{types-DWMpAtGy.d.mts → types-5P5B9RgV.d.mts} +57 -1
  99. package/dist/{types-DWMpAtGy.d.ts → types-5P5B9RgV.d.ts} +57 -1
  100. package/dist/{types-CGkyylOa.d.mts → types-DYyIAgQg.d.mts} +2 -0
  101. package/dist/{types-CGkyylOa.d.ts → types-DYyIAgQg.d.ts} +2 -0
  102. package/dist/{types-BxzT7yhf.d.ts → types-J7Z_FqmV.d.mts} +34 -0
  103. package/package.json +15 -1
  104. package/scripts/postinstall.cjs +67 -0
  105. package/dist/chunk-AWMEH65F.js.map +0 -1
  106. package/dist/chunk-DTVZJPVM.mjs.map +0 -1
  107. package/dist/chunk-GQKBGL2W.js.map +0 -1
  108. package/dist/chunk-ITPVKQB6.js.map +0 -1
  109. package/dist/chunk-LNMI6OMN.js.map +0 -1
  110. package/dist/chunk-OOZCN7AF.mjs.map +0 -1
  111. package/dist/chunk-WRCX2NKY.mjs.map +0 -1
  112. package/dist/chunk-Z6EHHJWU.mjs.map +0 -1
@@ -21,6 +21,13 @@ import { S as SecurityHeadersConfig } from '../securityHeaders-nwZ6nP4g.mjs';
21
21
  * ],
22
22
  * })
23
23
  * ```
24
+ *
25
+ * @example GEO — `Link: rel="describedby"` for `/llms.txt` on every page:
26
+ * ```ts
27
+ * export default withSiteKitConfig({
28
+ * llmsTxtDiscoveryLink: true,
29
+ * })
30
+ * ```
24
31
  */
25
32
 
26
33
  type NextConfig = Record<string, unknown>;
@@ -37,6 +44,16 @@ interface SiteKitNextConfig {
37
44
  staticCaching?: boolean;
38
45
  /** Any raw next.config overrides — deep merged last */
39
46
  nextConfig?: NextConfig;
47
+ /**
48
+ * When true, append `Link: rel="describedby"` for `/llms.txt` on all routes (machine discovery).
49
+ * Uses `NEXT_PUBLIC_SITE_URL`, then `SITE_BASE_URL`, then `VERCEL_URL` (https) when no explicit `siteUrl`.
50
+ * If you also set `nextConfig.headers`, that replaces this helper’s `headers()` — merge the `Link` value manually or call `buildAiDiscoveryHeaders`.
51
+ */
52
+ llmsTxtDiscoveryLink?: boolean | {
53
+ siteUrl?: string;
54
+ llmsPath?: string;
55
+ includeTypeParameter?: boolean;
56
+ };
40
57
  }
41
58
  declare function withSiteKitConfig(config?: SiteKitNextConfig): NextConfig;
42
59
 
@@ -21,6 +21,13 @@ import { S as SecurityHeadersConfig } from '../securityHeaders-nwZ6nP4g.js';
21
21
  * ],
22
22
  * })
23
23
  * ```
24
+ *
25
+ * @example GEO — `Link: rel="describedby"` for `/llms.txt` on every page:
26
+ * ```ts
27
+ * export default withSiteKitConfig({
28
+ * llmsTxtDiscoveryLink: true,
29
+ * })
30
+ * ```
24
31
  */
25
32
 
26
33
  type NextConfig = Record<string, unknown>;
@@ -37,6 +44,16 @@ interface SiteKitNextConfig {
37
44
  staticCaching?: boolean;
38
45
  /** Any raw next.config overrides — deep merged last */
39
46
  nextConfig?: NextConfig;
47
+ /**
48
+ * When true, append `Link: rel="describedby"` for `/llms.txt` on all routes (machine discovery).
49
+ * Uses `NEXT_PUBLIC_SITE_URL`, then `SITE_BASE_URL`, then `VERCEL_URL` (https) when no explicit `siteUrl`.
50
+ * If you also set `nextConfig.headers`, that replaces this helper’s `headers()` — merge the `Link` value manually or call `buildAiDiscoveryHeaders`.
51
+ */
52
+ llmsTxtDiscoveryLink?: boolean | {
53
+ siteUrl?: string;
54
+ llmsPath?: string;
55
+ includeTypeParameter?: boolean;
56
+ };
40
57
  }
41
58
  declare function withSiteKitConfig(config?: SiteKitNextConfig): NextConfig;
42
59
 
@@ -1,15 +1,30 @@
1
1
  'use strict';
2
2
 
3
3
  var chunk3MYZS6PD_js = require('../chunk-3MYZS6PD.js');
4
+ var chunk5B4FABFK_js = require('../chunk-5B4FABFK.js');
4
5
  require('../chunk-ZSMWDLMK.js');
5
6
 
6
7
  // src/config/index.ts
8
+ function resolvePublicSiteUrl(explicit) {
9
+ const a = process.env.NEXT_PUBLIC_SITE_URL?.trim();
10
+ if (a) return a.replace(/\/$/, "");
11
+ const b = process.env.SITE_BASE_URL?.trim();
12
+ if (b) return b.replace(/\/$/, "");
13
+ const v = process.env.VERCEL_URL?.trim();
14
+ if (v) return `https://${v.replace(/\/$/, "")}`;
15
+ return null;
16
+ }
7
17
  function withSiteKitConfig(config) {
8
18
  const securityHeaders = chunk3MYZS6PD_js.buildSecurityHeaders(
9
19
  config?.securityHeaders
10
20
  );
11
21
  const includeSecurityHeaders = config?.securityHeaders !== false;
12
22
  const includeStaticCaching = config?.staticCaching !== false;
23
+ const discoveryOpt = config?.llmsTxtDiscoveryLink;
24
+ const includeDiscovery = discoveryOpt === true || typeof discoveryOpt === "object" && discoveryOpt !== null;
25
+ const discoverySiteUrl = typeof discoveryOpt === "object" && discoveryOpt?.siteUrl?.trim() ? discoveryOpt.siteUrl.replace(/\/$/, "") : includeDiscovery ? resolvePublicSiteUrl() : null;
26
+ const discoveryLlmsPath = typeof discoveryOpt === "object" ? discoveryOpt.llmsPath : void 0;
27
+ const discoveryIncludeType = typeof discoveryOpt === "object" && discoveryOpt.includeTypeParameter !== void 0 ? discoveryOpt.includeTypeParameter : void 0;
13
28
  const result = {
14
29
  reactStrictMode: true,
15
30
  poweredByHeader: false,
@@ -36,12 +51,37 @@ function withSiteKitConfig(config) {
36
51
  async headers() {
37
52
  const headerGroups = [];
38
53
  if (includeSecurityHeaders) {
54
+ const baseHeaders = Object.entries(securityHeaders).map(
55
+ ([key, value]) => ({ key, value })
56
+ );
57
+ if (includeDiscovery && discoverySiteUrl) {
58
+ const linkMap = chunk5B4FABFK_js.buildAiDiscoveryHeaders({
59
+ siteUrl: discoverySiteUrl,
60
+ llmsPath: discoveryLlmsPath,
61
+ includeTypeParameter: discoveryIncludeType
62
+ });
63
+ const linkVal = linkMap.Link;
64
+ if (linkVal) {
65
+ baseHeaders.push({ key: "Link", value: linkVal });
66
+ }
67
+ }
39
68
  headerGroups.push({
40
69
  source: "/(.*)",
41
- headers: Object.entries(securityHeaders).map(
42
- ([key, value]) => ({ key, value })
43
- )
70
+ headers: baseHeaders
71
+ });
72
+ } else if (includeDiscovery && discoverySiteUrl) {
73
+ const linkMap = chunk5B4FABFK_js.buildAiDiscoveryHeaders({
74
+ siteUrl: discoverySiteUrl,
75
+ llmsPath: discoveryLlmsPath,
76
+ includeTypeParameter: discoveryIncludeType
44
77
  });
78
+ const linkVal = linkMap.Link;
79
+ if (linkVal) {
80
+ headerGroups.push({
81
+ source: "/(.*)",
82
+ headers: [{ key: "Link", value: linkVal }]
83
+ });
84
+ }
45
85
  }
46
86
  if (includeStaticCaching) {
47
87
  headerGroups.push({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/config/index.ts"],"names":["buildSecurityHeaders"],"mappings":";;;;;;AA6CO,SAAS,kBACd,MAAA,EACY;AACZ,EAAA,MAAM,eAAA,GAAkBA,qCAAA;AAAA,IACtB,MAAA,EAAQ;AAAA,GACV;AACA,EAAA,MAAM,sBAAA,GACJ,QAAQ,eAAA,KAAoB,KAAA;AAC9B,EAAA,MAAM,oBAAA,GAAuB,QAAQ,aAAA,KAAkB,KAAA;AAEvD,EAAA,MAAM,MAAA,GAAqB;AAAA,IACzB,eAAA,EAAiB,IAAA;AAAA,IACjB,eAAA,EAAiB,KAAA;AAAA,IACjB,MAAA,EAAQ;AAAA,MACN,OAAA,EAAS,CAAC,YAAA,EAAc,YAAY,CAAA;AAAA,MACpC,cAAA,EAAgB;AAAA,QACd;AAAA,UACE,QAAA,EAAU,OAAA;AAAA,UACV,QAAA,EAAU,eAAA;AAAA,UACV,QAAA,EAAU;AAAA,SACZ;AAAA,QACA;AAAA,UACE,QAAA,EAAU,OAAA;AAAA,UACV,QAAA,EAAU,cAAA;AAAA,UACV,QAAA,EAAU;AAAA,SACZ;AAAA,QACA,GAAI,MAAA,EAAQ,sBAAA,EAAwB,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,UAC9C,QAAA,EAAW,EAAE,QAAA,IAAY,OAAA;AAAA,UACzB,UAAU,CAAA,CAAE,QAAA;AAAA,UACZ,QAAA,EAAU,EAAE,QAAA,IAAY;AAAA,SAC1B,CAAE,KAAK;AAAC;AACV,KACF;AAAA,IACA,MAAM,OAAA,GAAU;AACd,MAAA,MAAM,eAGD,EAAC;AAGN,MAAA,IAAI,sBAAA,EAAwB;AAC1B,QAAA,YAAA,CAAa,IAAA,CAAK;AAAA,UAChB,MAAA,EAAQ,OAAA;AAAA,UACR,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,eAAe,CAAA,CAAE,GAAA;AAAA,YACvC,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,MAAO,EAAE,KAAK,KAAA,EAAM;AAAA;AAClC,SACD,CAAA;AAAA,MACH;AAGA,MAAA,IAAI,oBAAA,EAAsB;AACxB,QAAA,YAAA,CAAa,IAAA,CAAK;AAAA,UAChB,MAAA,EACE,4DAAA;AAAA,UACF,OAAA,EAAS;AAAA,YACP;AAAA,cACE,GAAA,EAAK,eAAA;AAAA,cACL,KAAA,EACE;AAAA;AACJ;AACF,SACD,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,YAAA;AAAA,IACT,CAAA;AAAA,IACA,GAAG,MAAA,EAAQ;AAAA,GACb;AAEA,EAAA,OAAO,MAAA;AACT","file":"index.js","sourcesContent":["/**\n * @sonordev/site-kit/config\n *\n * Next.js configuration helper. Provides a sensible default next.config\n * with security headers, image optimization, and static caching.\n *\n * @example\n * ```ts\n * // next.config.ts — entire file\n * import { withSiteKitConfig } from '@sonordev/site-kit/config'\n * export default withSiteKitConfig()\n * ```\n *\n * @example With custom image domains:\n * ```ts\n * export default withSiteKitConfig({\n * additionalImageDomains: [\n * { hostname: 'images.unsplash.com' },\n * ],\n * })\n * ```\n */\n\nimport { buildSecurityHeaders } from '../middleware/securityHeaders'\nimport type { SecurityHeadersConfig } from '../middleware/securityHeaders'\n\n// We can't import NextConfig at compile-time since next is a peer dep.\n// Use a compatible shape instead.\ntype NextConfig = Record<string, unknown>\n\nexport interface SiteKitNextConfig {\n /** Additional image domains for next/image optimization */\n additionalImageDomains?: Array<{\n hostname: string\n protocol?: 'https' | 'http'\n pathname?: string\n }>\n /** Security headers via next.config headers(). Default: true */\n securityHeaders?: boolean | SecurityHeadersConfig\n /** Cache headers for static assets. Default: true */\n staticCaching?: boolean\n /** Any raw next.config overrides — deep merged last */\n nextConfig?: NextConfig\n}\n\nexport function withSiteKitConfig(\n config?: SiteKitNextConfig,\n): NextConfig {\n const securityHeaders = buildSecurityHeaders(\n config?.securityHeaders,\n )\n const includeSecurityHeaders =\n config?.securityHeaders !== false\n const includeStaticCaching = config?.staticCaching !== false\n\n const result: NextConfig = {\n reactStrictMode: true,\n poweredByHeader: false,\n images: {\n formats: ['image/webp', 'image/avif'],\n remotePatterns: [\n {\n protocol: 'https',\n hostname: '*.supabase.co',\n pathname: '/**',\n },\n {\n protocol: 'https',\n hostname: 'api.sonor.io',\n pathname: '/**',\n },\n ...(config?.additionalImageDomains?.map((d) => ({\n protocol: (d.protocol || 'https') as 'https' | 'http',\n hostname: d.hostname,\n pathname: d.pathname || '/**',\n })) || []),\n ],\n },\n async headers() {\n const headerGroups: Array<{\n source: string\n headers: Array<{ key: string; value: string }>\n }> = []\n\n // Security headers on all routes\n if (includeSecurityHeaders) {\n headerGroups.push({\n source: '/(.*)',\n headers: Object.entries(securityHeaders).map(\n ([key, value]) => ({ key, value }),\n ),\n })\n }\n\n // Static asset caching\n if (includeStaticCaching) {\n headerGroups.push({\n source:\n '/:path*.(ico|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot)',\n headers: [\n {\n key: 'Cache-Control',\n value:\n 'public, max-age=31536000, immutable',\n },\n ],\n })\n }\n\n return headerGroups\n },\n ...config?.nextConfig,\n }\n\n return result\n}\n"]}
1
+ {"version":3,"sources":["../../src/config/index.ts"],"names":["buildSecurityHeaders","buildAiDiscoveryHeaders"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sonordev/site-kit/config\n *\n * Next.js configuration helper. Provides a sensible default next.config\n * with security headers, image optimization, and static caching.\n *\n * @example\n * ```ts\n * // next.config.ts — entire file\n * import { withSiteKitConfig } from '@sonordev/site-kit/config'\n * export default withSiteKitConfig()\n * ```\n *\n * @example With custom image domains:\n * ```ts\n * export default withSiteKitConfig({\n * additionalImageDomains: [\n * { hostname: 'images.unsplash.com' },\n * ],\n * })\n * ```\n *\n * @example GEO — `Link: rel=\"describedby\"` for `/llms.txt` on every page:\n * ```ts\n * export default withSiteKitConfig({\n * llmsTxtDiscoveryLink: true,\n * })\n * ```\n */\n\nimport { buildSecurityHeaders } from '../middleware/securityHeaders'\nimport type { SecurityHeadersConfig } from '../middleware/securityHeaders'\nimport { buildAiDiscoveryHeaders } from '../llms/discovery-headers'\n\n// We can't import NextConfig at compile-time since next is a peer dep.\n// Use a compatible shape instead.\ntype NextConfig = Record<string, unknown>\n\nexport interface SiteKitNextConfig {\n /** Additional image domains for next/image optimization */\n additionalImageDomains?: Array<{\n hostname: string\n protocol?: 'https' | 'http'\n pathname?: string\n }>\n /** Security headers via next.config headers(). Default: true */\n securityHeaders?: boolean | SecurityHeadersConfig\n /** Cache headers for static assets. Default: true */\n staticCaching?: boolean\n /** Any raw next.config overrides — deep merged last */\n nextConfig?: NextConfig\n /**\n * When true, append `Link: rel=\"describedby\"` for `/llms.txt` on all routes (machine discovery).\n * Uses `NEXT_PUBLIC_SITE_URL`, then `SITE_BASE_URL`, then `VERCEL_URL` (https) when no explicit `siteUrl`.\n * If you also set `nextConfig.headers`, that replaces this helper’s `headers()` — merge the `Link` value manually or call `buildAiDiscoveryHeaders`.\n */\n llmsTxtDiscoveryLink?:\n | boolean\n | { siteUrl?: string; llmsPath?: string; includeTypeParameter?: boolean }\n}\n\nfunction resolvePublicSiteUrl(\n explicit?: string,\n): string | null {\n if (explicit?.trim()) return explicit.replace(/\\/$/, '')\n const a = process.env.NEXT_PUBLIC_SITE_URL?.trim()\n if (a) return a.replace(/\\/$/, '')\n const b = process.env.SITE_BASE_URL?.trim()\n if (b) return b.replace(/\\/$/, '')\n const v = process.env.VERCEL_URL?.trim()\n if (v) return `https://${v.replace(/\\/$/, '')}`\n return null\n}\n\nexport function withSiteKitConfig(\n config?: SiteKitNextConfig,\n): NextConfig {\n const securityHeaders = buildSecurityHeaders(\n config?.securityHeaders,\n )\n const includeSecurityHeaders =\n config?.securityHeaders !== false\n const includeStaticCaching = config?.staticCaching !== false\n\n const discoveryOpt = config?.llmsTxtDiscoveryLink\n const includeDiscovery =\n discoveryOpt === true ||\n (typeof discoveryOpt === 'object' && discoveryOpt !== null)\n const discoverySiteUrl =\n typeof discoveryOpt === 'object' && discoveryOpt?.siteUrl?.trim()\n ? discoveryOpt.siteUrl.replace(/\\/$/, '')\n : includeDiscovery\n ? resolvePublicSiteUrl()\n : null\n const discoveryLlmsPath =\n typeof discoveryOpt === 'object' ? discoveryOpt.llmsPath : undefined\n const discoveryIncludeType =\n typeof discoveryOpt === 'object' && discoveryOpt.includeTypeParameter !== undefined\n ? discoveryOpt.includeTypeParameter\n : undefined\n\n const result: NextConfig = {\n reactStrictMode: true,\n poweredByHeader: false,\n images: {\n formats: ['image/webp', 'image/avif'],\n remotePatterns: [\n {\n protocol: 'https',\n hostname: '*.supabase.co',\n pathname: '/**',\n },\n {\n protocol: 'https',\n hostname: 'api.sonor.io',\n pathname: '/**',\n },\n ...(config?.additionalImageDomains?.map((d) => ({\n protocol: (d.protocol || 'https') as 'https' | 'http',\n hostname: d.hostname,\n pathname: d.pathname || '/**',\n })) || []),\n ],\n },\n async headers() {\n const headerGroups: Array<{\n source: string\n headers: Array<{ key: string; value: string }>\n }> = []\n\n // Security headers on all routes\n if (includeSecurityHeaders) {\n const baseHeaders = Object.entries(securityHeaders).map(\n ([key, value]) => ({ key, value }),\n )\n if (includeDiscovery && discoverySiteUrl) {\n const linkMap = buildAiDiscoveryHeaders({\n siteUrl: discoverySiteUrl,\n llmsPath: discoveryLlmsPath,\n includeTypeParameter: discoveryIncludeType,\n })\n const linkVal = linkMap.Link\n if (linkVal) {\n baseHeaders.push({ key: 'Link', value: linkVal })\n }\n }\n headerGroups.push({\n source: '/(.*)',\n headers: baseHeaders,\n })\n } else if (includeDiscovery && discoverySiteUrl) {\n const linkMap = buildAiDiscoveryHeaders({\n siteUrl: discoverySiteUrl,\n llmsPath: discoveryLlmsPath,\n includeTypeParameter: discoveryIncludeType,\n })\n const linkVal = linkMap.Link\n if (linkVal) {\n headerGroups.push({\n source: '/(.*)',\n headers: [{ key: 'Link', value: linkVal }],\n })\n }\n }\n\n // Static asset caching\n if (includeStaticCaching) {\n headerGroups.push({\n source:\n '/:path*.(ico|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot)',\n headers: [\n {\n key: 'Cache-Control',\n value:\n 'public, max-age=31536000, immutable',\n },\n ],\n })\n }\n\n return headerGroups\n },\n ...config?.nextConfig,\n }\n\n return result\n}\n"]}
@@ -1,13 +1,28 @@
1
1
  import { buildSecurityHeaders } from '../chunk-6BIPAKL4.mjs';
2
+ import { buildAiDiscoveryHeaders } from '../chunk-H4OBGC43.mjs';
2
3
  import '../chunk-4XPGGLVP.mjs';
3
4
 
4
5
  // src/config/index.ts
6
+ function resolvePublicSiteUrl(explicit) {
7
+ const a = process.env.NEXT_PUBLIC_SITE_URL?.trim();
8
+ if (a) return a.replace(/\/$/, "");
9
+ const b = process.env.SITE_BASE_URL?.trim();
10
+ if (b) return b.replace(/\/$/, "");
11
+ const v = process.env.VERCEL_URL?.trim();
12
+ if (v) return `https://${v.replace(/\/$/, "")}`;
13
+ return null;
14
+ }
5
15
  function withSiteKitConfig(config) {
6
16
  const securityHeaders = buildSecurityHeaders(
7
17
  config?.securityHeaders
8
18
  );
9
19
  const includeSecurityHeaders = config?.securityHeaders !== false;
10
20
  const includeStaticCaching = config?.staticCaching !== false;
21
+ const discoveryOpt = config?.llmsTxtDiscoveryLink;
22
+ const includeDiscovery = discoveryOpt === true || typeof discoveryOpt === "object" && discoveryOpt !== null;
23
+ const discoverySiteUrl = typeof discoveryOpt === "object" && discoveryOpt?.siteUrl?.trim() ? discoveryOpt.siteUrl.replace(/\/$/, "") : includeDiscovery ? resolvePublicSiteUrl() : null;
24
+ const discoveryLlmsPath = typeof discoveryOpt === "object" ? discoveryOpt.llmsPath : void 0;
25
+ const discoveryIncludeType = typeof discoveryOpt === "object" && discoveryOpt.includeTypeParameter !== void 0 ? discoveryOpt.includeTypeParameter : void 0;
11
26
  const result = {
12
27
  reactStrictMode: true,
13
28
  poweredByHeader: false,
@@ -34,12 +49,37 @@ function withSiteKitConfig(config) {
34
49
  async headers() {
35
50
  const headerGroups = [];
36
51
  if (includeSecurityHeaders) {
52
+ const baseHeaders = Object.entries(securityHeaders).map(
53
+ ([key, value]) => ({ key, value })
54
+ );
55
+ if (includeDiscovery && discoverySiteUrl) {
56
+ const linkMap = buildAiDiscoveryHeaders({
57
+ siteUrl: discoverySiteUrl,
58
+ llmsPath: discoveryLlmsPath,
59
+ includeTypeParameter: discoveryIncludeType
60
+ });
61
+ const linkVal = linkMap.Link;
62
+ if (linkVal) {
63
+ baseHeaders.push({ key: "Link", value: linkVal });
64
+ }
65
+ }
37
66
  headerGroups.push({
38
67
  source: "/(.*)",
39
- headers: Object.entries(securityHeaders).map(
40
- ([key, value]) => ({ key, value })
41
- )
68
+ headers: baseHeaders
69
+ });
70
+ } else if (includeDiscovery && discoverySiteUrl) {
71
+ const linkMap = buildAiDiscoveryHeaders({
72
+ siteUrl: discoverySiteUrl,
73
+ llmsPath: discoveryLlmsPath,
74
+ includeTypeParameter: discoveryIncludeType
42
75
  });
76
+ const linkVal = linkMap.Link;
77
+ if (linkVal) {
78
+ headerGroups.push({
79
+ source: "/(.*)",
80
+ headers: [{ key: "Link", value: linkVal }]
81
+ });
82
+ }
43
83
  }
44
84
  if (includeStaticCaching) {
45
85
  headerGroups.push({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/config/index.ts"],"names":[],"mappings":";;;;AA6CO,SAAS,kBACd,MAAA,EACY;AACZ,EAAA,MAAM,eAAA,GAAkB,oBAAA;AAAA,IACtB,MAAA,EAAQ;AAAA,GACV;AACA,EAAA,MAAM,sBAAA,GACJ,QAAQ,eAAA,KAAoB,KAAA;AAC9B,EAAA,MAAM,oBAAA,GAAuB,QAAQ,aAAA,KAAkB,KAAA;AAEvD,EAAA,MAAM,MAAA,GAAqB;AAAA,IACzB,eAAA,EAAiB,IAAA;AAAA,IACjB,eAAA,EAAiB,KAAA;AAAA,IACjB,MAAA,EAAQ;AAAA,MACN,OAAA,EAAS,CAAC,YAAA,EAAc,YAAY,CAAA;AAAA,MACpC,cAAA,EAAgB;AAAA,QACd;AAAA,UACE,QAAA,EAAU,OAAA;AAAA,UACV,QAAA,EAAU,eAAA;AAAA,UACV,QAAA,EAAU;AAAA,SACZ;AAAA,QACA;AAAA,UACE,QAAA,EAAU,OAAA;AAAA,UACV,QAAA,EAAU,cAAA;AAAA,UACV,QAAA,EAAU;AAAA,SACZ;AAAA,QACA,GAAI,MAAA,EAAQ,sBAAA,EAAwB,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,UAC9C,QAAA,EAAW,EAAE,QAAA,IAAY,OAAA;AAAA,UACzB,UAAU,CAAA,CAAE,QAAA;AAAA,UACZ,QAAA,EAAU,EAAE,QAAA,IAAY;AAAA,SAC1B,CAAE,KAAK;AAAC;AACV,KACF;AAAA,IACA,MAAM,OAAA,GAAU;AACd,MAAA,MAAM,eAGD,EAAC;AAGN,MAAA,IAAI,sBAAA,EAAwB;AAC1B,QAAA,YAAA,CAAa,IAAA,CAAK;AAAA,UAChB,MAAA,EAAQ,OAAA;AAAA,UACR,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,eAAe,CAAA,CAAE,GAAA;AAAA,YACvC,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,MAAO,EAAE,KAAK,KAAA,EAAM;AAAA;AAClC,SACD,CAAA;AAAA,MACH;AAGA,MAAA,IAAI,oBAAA,EAAsB;AACxB,QAAA,YAAA,CAAa,IAAA,CAAK;AAAA,UAChB,MAAA,EACE,4DAAA;AAAA,UACF,OAAA,EAAS;AAAA,YACP;AAAA,cACE,GAAA,EAAK,eAAA;AAAA,cACL,KAAA,EACE;AAAA;AACJ;AACF,SACD,CAAA;AAAA,MACH;AAEA,MAAA,OAAO,YAAA;AAAA,IACT,CAAA;AAAA,IACA,GAAG,MAAA,EAAQ;AAAA,GACb;AAEA,EAAA,OAAO,MAAA;AACT","file":"index.mjs","sourcesContent":["/**\n * @sonordev/site-kit/config\n *\n * Next.js configuration helper. Provides a sensible default next.config\n * with security headers, image optimization, and static caching.\n *\n * @example\n * ```ts\n * // next.config.ts — entire file\n * import { withSiteKitConfig } from '@sonordev/site-kit/config'\n * export default withSiteKitConfig()\n * ```\n *\n * @example With custom image domains:\n * ```ts\n * export default withSiteKitConfig({\n * additionalImageDomains: [\n * { hostname: 'images.unsplash.com' },\n * ],\n * })\n * ```\n */\n\nimport { buildSecurityHeaders } from '../middleware/securityHeaders'\nimport type { SecurityHeadersConfig } from '../middleware/securityHeaders'\n\n// We can't import NextConfig at compile-time since next is a peer dep.\n// Use a compatible shape instead.\ntype NextConfig = Record<string, unknown>\n\nexport interface SiteKitNextConfig {\n /** Additional image domains for next/image optimization */\n additionalImageDomains?: Array<{\n hostname: string\n protocol?: 'https' | 'http'\n pathname?: string\n }>\n /** Security headers via next.config headers(). Default: true */\n securityHeaders?: boolean | SecurityHeadersConfig\n /** Cache headers for static assets. Default: true */\n staticCaching?: boolean\n /** Any raw next.config overrides — deep merged last */\n nextConfig?: NextConfig\n}\n\nexport function withSiteKitConfig(\n config?: SiteKitNextConfig,\n): NextConfig {\n const securityHeaders = buildSecurityHeaders(\n config?.securityHeaders,\n )\n const includeSecurityHeaders =\n config?.securityHeaders !== false\n const includeStaticCaching = config?.staticCaching !== false\n\n const result: NextConfig = {\n reactStrictMode: true,\n poweredByHeader: false,\n images: {\n formats: ['image/webp', 'image/avif'],\n remotePatterns: [\n {\n protocol: 'https',\n hostname: '*.supabase.co',\n pathname: '/**',\n },\n {\n protocol: 'https',\n hostname: 'api.sonor.io',\n pathname: '/**',\n },\n ...(config?.additionalImageDomains?.map((d) => ({\n protocol: (d.protocol || 'https') as 'https' | 'http',\n hostname: d.hostname,\n pathname: d.pathname || '/**',\n })) || []),\n ],\n },\n async headers() {\n const headerGroups: Array<{\n source: string\n headers: Array<{ key: string; value: string }>\n }> = []\n\n // Security headers on all routes\n if (includeSecurityHeaders) {\n headerGroups.push({\n source: '/(.*)',\n headers: Object.entries(securityHeaders).map(\n ([key, value]) => ({ key, value }),\n ),\n })\n }\n\n // Static asset caching\n if (includeStaticCaching) {\n headerGroups.push({\n source:\n '/:path*.(ico|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot)',\n headers: [\n {\n key: 'Cache-Control',\n value:\n 'public, max-age=31536000, immutable',\n },\n ],\n })\n }\n\n return headerGroups\n },\n ...config?.nextConfig,\n }\n\n return result\n}\n"]}
1
+ {"version":3,"sources":["../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @sonordev/site-kit/config\n *\n * Next.js configuration helper. Provides a sensible default next.config\n * with security headers, image optimization, and static caching.\n *\n * @example\n * ```ts\n * // next.config.ts — entire file\n * import { withSiteKitConfig } from '@sonordev/site-kit/config'\n * export default withSiteKitConfig()\n * ```\n *\n * @example With custom image domains:\n * ```ts\n * export default withSiteKitConfig({\n * additionalImageDomains: [\n * { hostname: 'images.unsplash.com' },\n * ],\n * })\n * ```\n *\n * @example GEO — `Link: rel=\"describedby\"` for `/llms.txt` on every page:\n * ```ts\n * export default withSiteKitConfig({\n * llmsTxtDiscoveryLink: true,\n * })\n * ```\n */\n\nimport { buildSecurityHeaders } from '../middleware/securityHeaders'\nimport type { SecurityHeadersConfig } from '../middleware/securityHeaders'\nimport { buildAiDiscoveryHeaders } from '../llms/discovery-headers'\n\n// We can't import NextConfig at compile-time since next is a peer dep.\n// Use a compatible shape instead.\ntype NextConfig = Record<string, unknown>\n\nexport interface SiteKitNextConfig {\n /** Additional image domains for next/image optimization */\n additionalImageDomains?: Array<{\n hostname: string\n protocol?: 'https' | 'http'\n pathname?: string\n }>\n /** Security headers via next.config headers(). Default: true */\n securityHeaders?: boolean | SecurityHeadersConfig\n /** Cache headers for static assets. Default: true */\n staticCaching?: boolean\n /** Any raw next.config overrides — deep merged last */\n nextConfig?: NextConfig\n /**\n * When true, append `Link: rel=\"describedby\"` for `/llms.txt` on all routes (machine discovery).\n * Uses `NEXT_PUBLIC_SITE_URL`, then `SITE_BASE_URL`, then `VERCEL_URL` (https) when no explicit `siteUrl`.\n * If you also set `nextConfig.headers`, that replaces this helper’s `headers()` — merge the `Link` value manually or call `buildAiDiscoveryHeaders`.\n */\n llmsTxtDiscoveryLink?:\n | boolean\n | { siteUrl?: string; llmsPath?: string; includeTypeParameter?: boolean }\n}\n\nfunction resolvePublicSiteUrl(\n explicit?: string,\n): string | null {\n if (explicit?.trim()) return explicit.replace(/\\/$/, '')\n const a = process.env.NEXT_PUBLIC_SITE_URL?.trim()\n if (a) return a.replace(/\\/$/, '')\n const b = process.env.SITE_BASE_URL?.trim()\n if (b) return b.replace(/\\/$/, '')\n const v = process.env.VERCEL_URL?.trim()\n if (v) return `https://${v.replace(/\\/$/, '')}`\n return null\n}\n\nexport function withSiteKitConfig(\n config?: SiteKitNextConfig,\n): NextConfig {\n const securityHeaders = buildSecurityHeaders(\n config?.securityHeaders,\n )\n const includeSecurityHeaders =\n config?.securityHeaders !== false\n const includeStaticCaching = config?.staticCaching !== false\n\n const discoveryOpt = config?.llmsTxtDiscoveryLink\n const includeDiscovery =\n discoveryOpt === true ||\n (typeof discoveryOpt === 'object' && discoveryOpt !== null)\n const discoverySiteUrl =\n typeof discoveryOpt === 'object' && discoveryOpt?.siteUrl?.trim()\n ? discoveryOpt.siteUrl.replace(/\\/$/, '')\n : includeDiscovery\n ? resolvePublicSiteUrl()\n : null\n const discoveryLlmsPath =\n typeof discoveryOpt === 'object' ? discoveryOpt.llmsPath : undefined\n const discoveryIncludeType =\n typeof discoveryOpt === 'object' && discoveryOpt.includeTypeParameter !== undefined\n ? discoveryOpt.includeTypeParameter\n : undefined\n\n const result: NextConfig = {\n reactStrictMode: true,\n poweredByHeader: false,\n images: {\n formats: ['image/webp', 'image/avif'],\n remotePatterns: [\n {\n protocol: 'https',\n hostname: '*.supabase.co',\n pathname: '/**',\n },\n {\n protocol: 'https',\n hostname: 'api.sonor.io',\n pathname: '/**',\n },\n ...(config?.additionalImageDomains?.map((d) => ({\n protocol: (d.protocol || 'https') as 'https' | 'http',\n hostname: d.hostname,\n pathname: d.pathname || '/**',\n })) || []),\n ],\n },\n async headers() {\n const headerGroups: Array<{\n source: string\n headers: Array<{ key: string; value: string }>\n }> = []\n\n // Security headers on all routes\n if (includeSecurityHeaders) {\n const baseHeaders = Object.entries(securityHeaders).map(\n ([key, value]) => ({ key, value }),\n )\n if (includeDiscovery && discoverySiteUrl) {\n const linkMap = buildAiDiscoveryHeaders({\n siteUrl: discoverySiteUrl,\n llmsPath: discoveryLlmsPath,\n includeTypeParameter: discoveryIncludeType,\n })\n const linkVal = linkMap.Link\n if (linkVal) {\n baseHeaders.push({ key: 'Link', value: linkVal })\n }\n }\n headerGroups.push({\n source: '/(.*)',\n headers: baseHeaders,\n })\n } else if (includeDiscovery && discoverySiteUrl) {\n const linkMap = buildAiDiscoveryHeaders({\n siteUrl: discoverySiteUrl,\n llmsPath: discoveryLlmsPath,\n includeTypeParameter: discoveryIncludeType,\n })\n const linkVal = linkMap.Link\n if (linkVal) {\n headerGroups.push({\n source: '/(.*)',\n headers: [{ key: 'Link', value: linkVal }],\n })\n }\n }\n\n // Static asset caching\n if (includeStaticCaching) {\n headerGroups.push({\n source:\n '/:path*.(ico|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot)',\n headers: [\n {\n key: 'Cache-Control',\n value:\n 'public, max-age=31536000, immutable',\n },\n ],\n })\n }\n\n return headerGroups\n },\n ...config?.nextConfig,\n }\n\n return result\n}\n"]}
@@ -1416,6 +1416,7 @@ function FormClient({
1416
1416
  const [isComplete, setIsComplete] = react.useState(false);
1417
1417
  const [honeypotValue, setHoneypotValue] = react.useState("");
1418
1418
  const honeypotFieldName = config2.honeypot_field || "website";
1419
+ const formLoadedAt = react.useRef(Date.now());
1419
1420
  const { trackStepChange, trackComplete } = useFormTracking({
1420
1421
  formId: config2.id,
1421
1422
  totalSteps: config2.total_steps
@@ -1561,7 +1562,8 @@ function FormClient({
1561
1562
  recaptchaToken: recaptchaToken || void 0,
1562
1563
  utmSource: utm.utm_source,
1563
1564
  utmMedium: utm.utm_medium,
1564
- utmCampaign: utm.utm_campaign
1565
+ utmCampaign: utm.utm_campaign,
1566
+ _formLoadedAt: formLoadedAt.current
1565
1567
  }
1566
1568
  };
1567
1569
  const response = await fetch(`${apiUrl}/api/public/forms/submit`, {