heliumts 0.7.3 → 0.7.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.
@@ -21,7 +21,7 @@ export class SEOMetadataRouter {
21
21
  return this.pageRouteMatchers.some((matcher) => matcher(pathname) !== null);
22
22
  }
23
23
  async resolve(req, ctx, targetPath) {
24
- const method = req.method?.toUpperCase() || "GET";
24
+ const method = targetPath ? "GET" : req.method?.toUpperCase() || "GET";
25
25
  if (method !== "GET") {
26
26
  return null;
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"seoMetadataRouter.js","sourceRoot":"","sources":["../../src/server/seoMetadataRouter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,KAAK,CAAC;AAmBxC,MAAM,OAAO,iBAAiB;IAA9B;QACY,WAAM,GAAsB,EAAE,CAAC;QAC/B,sBAAiB,GAAmB,EAAE,CAAC;IAwEnD,CAAC;IAtEG,cAAc,CAAC,MAA0B;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;SACjD,CAAC,CAAC,CAAC;IACR,CAAC;IAED,oBAAoB,CAAC,QAAkB;QACnC,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzI,CAAC;IAED,iBAAiB,CAAC,QAAgB;QAC9B,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAoB,EAAE,GAAkB,EAAE,UAAmB;QACvE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;QAClD,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACpE,MAAM,QAAQ,GAAG,aAAa,CAAE,YAAY,EAAE,QAA+B,IAAI,SAAS,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;QAC5G,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAkD,EAAE,CAAC;QAClE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;QACvC,CAAC;QAED,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,MAAM,WAAW,GAAI,YAAY,EAAE,KAA6C,IAAI,SAAS,CAAC,KAAK,CAAC;QACpG,IAAI,WAAW,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACzE,CAAC;YACL,CAAC;QACL,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,SAAS;YACb,CAAC;YAED,MAAM,eAAe,GAAuB;gBACxC,MAAM;gBACN,IAAI,EAAE,QAAQ;gBACd,OAAO;gBACP,KAAK;gBACL,MAAM,EAAE,KAAK,CAAC,MAAM;aACvB,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACnE,IAAI,QAAQ,EAAE,CAAC;gBACX,OAAO,QAAQ,CAAC;YACpB,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC7C,MAAM,iBAAiB,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAErE,OAAO,CAAC,QAAgB,EAAE,EAAE;QACxB,MAAM,kBAAkB,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,MAAM,GAAsC,EAAE,CAAC;QAErD,IAAI,iBAAiB,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,kBAAkB,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1D,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC;YAC/E,MAAM,OAAO,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAEtC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,EAAE,MAAM,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBAClB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC;gBAChB,CAAC;gBACD,SAAS,IAAI,CAAC,CAAC;gBACf,SAAS;YACb,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvF,IAAI,GAAG,EAAE,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACvB,CAAC;gBACD,OAAO,EAAE,MAAM,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC;gBAChB,CAAC;gBACD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;gBACzD,SAAS,IAAI,CAAC,CAAC;gBACf,SAAS;YACb,CAAC;YAED,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACpB,OAAO,IAAI,CAAC;YAChB,CAAC;YAED,SAAS,IAAI,CAAC,CAAC;QACnB,CAAC;QAED,IAAI,SAAS,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,CAAC;IACtB,CAAC,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAChC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;IAC9D,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IACzF,OAAO,UAAU,IAAI,GAAG,CAAC;AAC7B,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IACzC,IAAI,CAAC;QACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC","sourcesContent":["import type { IncomingMessage } from \"http\";\nimport { parse as parseUrl } from \"url\";\n\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumSEOMetadataDef, SEOMetadataRequest } from \"./defineSEOMetadata.js\";\nimport type { SocialMeta } from \"./meta.js\";\n\nexport interface SEOMetadataRoute {\n name: string;\n handler: HeliumSEOMetadataDef;\n}\n\ntype RouteMatcher = (pathname: string) => { params: Record<string, string | string[]> } | null;\n\ninterface RegisteredRoute {\n name: string;\n handler: HeliumSEOMetadataDef;\n matcher: RouteMatcher;\n}\n\nexport class SEOMetadataRouter {\n private routes: RegisteredRoute[] = [];\n private pageRouteMatchers: RouteMatcher[] = [];\n\n registerRoutes(routes: SEOMetadataRoute[]) {\n this.routes = routes.map((route) => ({\n name: route.name,\n handler: route.handler,\n matcher: createPathMatcher(route.handler.path),\n }));\n }\n\n setPageRoutePatterns(patterns: string[]) {\n this.pageRouteMatchers = patterns.filter((pattern) => pattern && pattern !== \"__404__\").map((pattern) => createPathMatcher(pattern));\n }\n\n hasPageRouteMatch(pathname: string): boolean {\n if (this.pageRouteMatchers.length === 0) {\n return false;\n }\n\n return this.pageRouteMatchers.some((matcher) => matcher(pathname) !== null);\n }\n\n async resolve(req: IncomingMessage, ctx: HeliumContext, targetPath?: string): Promise<SocialMeta | null> {\n const method = req.method?.toUpperCase() || \"GET\";\n if (method !== \"GET\") {\n return null;\n }\n\n const parsedUrl = parseUrl(req.url || \"\", true);\n const targetParsed = targetPath ? parseUrl(targetPath, true) : null;\n const pathname = normalizePath((targetParsed?.pathname as string | undefined) || parsedUrl.pathname || \"/\");\n if (!this.hasPageRouteMatch(pathname)) {\n return null;\n }\n\n const headers: Record<string, string | string[] | undefined> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n headers[key.toLowerCase()] = value;\n }\n\n const query: Record<string, string> = {};\n const querySource = (targetParsed?.query as Record<string, unknown> | undefined) ?? parsedUrl.query;\n if (querySource) {\n for (const [key, value] of Object.entries(querySource)) {\n if (value !== undefined) {\n query[key] = Array.isArray(value) ? String(value[0]) : String(value);\n }\n }\n }\n\n for (const route of this.routes) {\n const match = route.matcher(pathname);\n if (!match) {\n continue;\n }\n\n const metadataRequest: SEOMetadataRequest = {\n method,\n path: pathname,\n headers,\n query,\n params: match.params,\n };\n\n const metadata = await route.handler.handler(metadataRequest, ctx);\n if (metadata) {\n return metadata;\n }\n }\n\n return null;\n }\n}\n\nexport function createPathMatcher(pattern: string): RouteMatcher {\n const normalizedPattern = normalizePath(pattern);\n const patternSegments = normalizedPattern.split(\"/\").filter(Boolean);\n\n return (pathname: string) => {\n const normalizedPathname = normalizePath(pathname);\n const pathSegments = normalizedPathname.split(\"/\").filter(Boolean);\n const params: Record<string, string | string[]> = {};\n\n if (normalizedPattern === \"/\") {\n return normalizedPathname === \"/\" ? { params } : null;\n }\n\n let pathIndex = 0;\n for (let patternIndex = 0; patternIndex < patternSegments.length; patternIndex++) {\n const segment = patternSegments[patternIndex];\n const value = pathSegments[pathIndex];\n\n if (segment === \"**\") {\n return { params };\n }\n\n if (segment === \"*\") {\n if (value === undefined) {\n return null;\n }\n pathIndex += 1;\n continue;\n }\n\n if (segment.startsWith(\"*\")) {\n const key = segment.slice(1);\n const rest = pathSegments.slice(pathIndex).map((part) => safeDecodeURIComponent(part));\n if (key) {\n params[key] = rest;\n }\n return { params };\n }\n\n if (segment.startsWith(\":\")) {\n if (value === undefined) {\n return null;\n }\n params[segment.slice(1)] = safeDecodeURIComponent(value);\n pathIndex += 1;\n continue;\n }\n\n if (segment !== value) {\n return null;\n }\n\n pathIndex += 1;\n }\n\n if (pathIndex !== pathSegments.length) {\n return null;\n }\n\n return { params };\n };\n}\n\nfunction normalizePath(value: string): string {\n const withoutQuery = value.split(\"?\")[0].split(\"#\")[0] || \"/\";\n if (withoutQuery === \"/\") {\n return \"/\";\n }\n\n const normalized = withoutQuery.endsWith(\"/\") ? withoutQuery.slice(0, -1) : withoutQuery;\n return normalized || \"/\";\n}\n\nfunction safeDecodeURIComponent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n"]}
1
+ {"version":3,"file":"seoMetadataRouter.js","sourceRoot":"","sources":["../../src/server/seoMetadataRouter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,KAAK,CAAC;AAmBxC,MAAM,OAAO,iBAAiB;IAA9B;QACY,WAAM,GAAsB,EAAE,CAAC;QAC/B,sBAAiB,GAAmB,EAAE,CAAC;IAwEnD,CAAC;IAtEG,cAAc,CAAC,MAA0B;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;SACjD,CAAC,CAAC,CAAC;IACR,CAAC;IAED,oBAAoB,CAAC,QAAkB;QACnC,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IACzI,CAAC;IAED,iBAAiB,CAAC,QAAgB;QAC9B,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAoB,EAAE,GAAkB,EAAE,UAAmB;QACvE,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;QACvE,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACpE,MAAM,QAAQ,GAAG,aAAa,CAAE,YAAY,EAAE,QAA+B,IAAI,SAAS,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;QAC5G,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAkD,EAAE,CAAC;QAClE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;QACvC,CAAC;QAED,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,MAAM,WAAW,GAAI,YAAY,EAAE,KAA6C,IAAI,SAAS,CAAC,KAAK,CAAC;QACpG,IAAI,WAAW,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACzE,CAAC;YACL,CAAC;QACL,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,SAAS;YACb,CAAC;YAED,MAAM,eAAe,GAAuB;gBACxC,MAAM;gBACN,IAAI,EAAE,QAAQ;gBACd,OAAO;gBACP,KAAK;gBACL,MAAM,EAAE,KAAK,CAAC,MAAM;aACvB,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACnE,IAAI,QAAQ,EAAE,CAAC;gBACX,OAAO,QAAQ,CAAC;YACpB,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC7C,MAAM,iBAAiB,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAErE,OAAO,CAAC,QAAgB,EAAE,EAAE;QACxB,MAAM,kBAAkB,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,MAAM,GAAsC,EAAE,CAAC;QAErD,IAAI,iBAAiB,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,kBAAkB,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1D,CAAC;QAED,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC;YAC/E,MAAM,OAAO,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAEtC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,EAAE,MAAM,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBAClB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC;gBAChB,CAAC;gBACD,SAAS,IAAI,CAAC,CAAC;gBACf,SAAS;YACb,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvF,IAAI,GAAG,EAAE,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACvB,CAAC;gBACD,OAAO,EAAE,MAAM,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC;gBAChB,CAAC;gBACD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;gBACzD,SAAS,IAAI,CAAC,CAAC;gBACf,SAAS;YACb,CAAC;YAED,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACpB,OAAO,IAAI,CAAC;YAChB,CAAC;YAED,SAAS,IAAI,CAAC,CAAC;QACnB,CAAC;QAED,IAAI,SAAS,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,CAAC;IACtB,CAAC,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAChC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;IAC9D,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IACzF,OAAO,UAAU,IAAI,GAAG,CAAC;AAC7B,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IACzC,IAAI,CAAC;QACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC","sourcesContent":["import type { IncomingMessage } from \"http\";\nimport { parse as parseUrl } from \"url\";\n\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumSEOMetadataDef, SEOMetadataRequest } from \"./defineSEOMetadata.js\";\nimport type { SocialMeta } from \"./meta.js\";\n\nexport interface SEOMetadataRoute {\n name: string;\n handler: HeliumSEOMetadataDef;\n}\n\ntype RouteMatcher = (pathname: string) => { params: Record<string, string | string[]> } | null;\n\ninterface RegisteredRoute {\n name: string;\n handler: HeliumSEOMetadataDef;\n matcher: RouteMatcher;\n}\n\nexport class SEOMetadataRouter {\n private routes: RegisteredRoute[] = [];\n private pageRouteMatchers: RouteMatcher[] = [];\n\n registerRoutes(routes: SEOMetadataRoute[]) {\n this.routes = routes.map((route) => ({\n name: route.name,\n handler: route.handler,\n matcher: createPathMatcher(route.handler.path),\n }));\n }\n\n setPageRoutePatterns(patterns: string[]) {\n this.pageRouteMatchers = patterns.filter((pattern) => pattern && pattern !== \"__404__\").map((pattern) => createPathMatcher(pattern));\n }\n\n hasPageRouteMatch(pathname: string): boolean {\n if (this.pageRouteMatchers.length === 0) {\n return false;\n }\n\n return this.pageRouteMatchers.some((matcher) => matcher(pathname) !== null);\n }\n\n async resolve(req: IncomingMessage, ctx: HeliumContext, targetPath?: string): Promise<SocialMeta | null> {\n const method = targetPath ? \"GET\" : req.method?.toUpperCase() || \"GET\";\n if (method !== \"GET\") {\n return null;\n }\n\n const parsedUrl = parseUrl(req.url || \"\", true);\n const targetParsed = targetPath ? parseUrl(targetPath, true) : null;\n const pathname = normalizePath((targetParsed?.pathname as string | undefined) || parsedUrl.pathname || \"/\");\n if (!this.hasPageRouteMatch(pathname)) {\n return null;\n }\n\n const headers: Record<string, string | string[] | undefined> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n headers[key.toLowerCase()] = value;\n }\n\n const query: Record<string, string> = {};\n const querySource = (targetParsed?.query as Record<string, unknown> | undefined) ?? parsedUrl.query;\n if (querySource) {\n for (const [key, value] of Object.entries(querySource)) {\n if (value !== undefined) {\n query[key] = Array.isArray(value) ? String(value[0]) : String(value);\n }\n }\n }\n\n for (const route of this.routes) {\n const match = route.matcher(pathname);\n if (!match) {\n continue;\n }\n\n const metadataRequest: SEOMetadataRequest = {\n method,\n path: pathname,\n headers,\n query,\n params: match.params,\n };\n\n const metadata = await route.handler.handler(metadataRequest, ctx);\n if (metadata) {\n return metadata;\n }\n }\n\n return null;\n }\n}\n\nexport function createPathMatcher(pattern: string): RouteMatcher {\n const normalizedPattern = normalizePath(pattern);\n const patternSegments = normalizedPattern.split(\"/\").filter(Boolean);\n\n return (pathname: string) => {\n const normalizedPathname = normalizePath(pathname);\n const pathSegments = normalizedPathname.split(\"/\").filter(Boolean);\n const params: Record<string, string | string[]> = {};\n\n if (normalizedPattern === \"/\") {\n return normalizedPathname === \"/\" ? { params } : null;\n }\n\n let pathIndex = 0;\n for (let patternIndex = 0; patternIndex < patternSegments.length; patternIndex++) {\n const segment = patternSegments[patternIndex];\n const value = pathSegments[pathIndex];\n\n if (segment === \"**\") {\n return { params };\n }\n\n if (segment === \"*\") {\n if (value === undefined) {\n return null;\n }\n pathIndex += 1;\n continue;\n }\n\n if (segment.startsWith(\"*\")) {\n const key = segment.slice(1);\n const rest = pathSegments.slice(pathIndex).map((part) => safeDecodeURIComponent(part));\n if (key) {\n params[key] = rest;\n }\n return { params };\n }\n\n if (segment.startsWith(\":\")) {\n if (value === undefined) {\n return null;\n }\n params[segment.slice(1)] = safeDecodeURIComponent(value);\n pathIndex += 1;\n continue;\n }\n\n if (segment !== value) {\n return null;\n }\n\n pathIndex += 1;\n }\n\n if (pathIndex !== pathSegments.length) {\n return null;\n }\n\n return { params };\n };\n}\n\nfunction normalizePath(value: string): string {\n const withoutQuery = value.split(\"?\")[0].split(\"#\")[0] || \"/\";\n if (withoutQuery === \"/\") {\n return \"/\";\n }\n\n const normalized = withoutQuery.endsWith(\"/\") ? withoutQuery.slice(0, -1) : withoutQuery;\n return normalized || \"/\";\n}\n\nfunction safeDecodeURIComponent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heliumts",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",