@mandujs/core 0.30.0 → 0.32.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.
- package/package.json +7 -1
- package/src/client/index.ts +11 -1
- package/src/client/rpc.ts +293 -140
- package/src/config/mandu.ts +107 -1
- package/src/config/validate.ts +145 -1
- package/src/contract/index.ts +18 -0
- package/src/contract/rpc.ts +443 -0
- package/src/filling/context.ts +60 -0
- package/src/guard/check.ts +225 -1
- package/src/guard/define-rule.ts +243 -0
- package/src/guard/index.ts +26 -0
- package/src/guard/rule-presets.ts +379 -0
- package/src/i18n/define.ts +126 -0
- package/src/i18n/index.ts +52 -0
- package/src/i18n/locale-resolver.ts +214 -0
- package/src/i18n/message-registry.ts +173 -0
- package/src/i18n/types.ts +112 -0
- package/src/middleware/index.ts +7 -0
- package/src/middleware/scheduler-cron.ts +96 -0
- package/src/router/fs-scanner.ts +101 -0
- package/src/router/index.ts +7 -1
- package/src/runtime/server.ts +432 -9
- package/src/runtime/ssr.ts +9 -0
- package/src/scheduler/index.ts +547 -343
- package/src/scheduler/validate.ts +169 -0
package/src/router/fs-scanner.ts
CHANGED
|
@@ -619,6 +619,107 @@ function escapeRegex(char: string): string {
|
|
|
619
619
|
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
623
|
+
// Phase 18.μ — i18n path-prefix route synthesis
|
|
624
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Options for {@link synthesizeLocaleRoutes}. Mirrors the relevant subset
|
|
628
|
+
* of `I18nDefinition` so callers don't need to pull the whole
|
|
629
|
+
* `@mandujs/core/i18n` surface into pure-router code paths.
|
|
630
|
+
*/
|
|
631
|
+
export interface LocaleSynthesisOptions {
|
|
632
|
+
/** Allow-list of locale codes to materialize. */
|
|
633
|
+
locales: readonly string[];
|
|
634
|
+
/** Default locale — its routes stay unprefixed (Next.js parity). */
|
|
635
|
+
defaultLocale: string;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Phase 18.μ — synthesize per-locale route variants at manifest-build
|
|
640
|
+
* time. Given a set of scanned routes, produces `locales.length` copies
|
|
641
|
+
* for every `page` / `api` route with a locale prefix baked into
|
|
642
|
+
* `id` + `pattern`. The default locale's routes are emitted unprefixed
|
|
643
|
+
* (so legacy links keep working and SEO stays intact).
|
|
644
|
+
*
|
|
645
|
+
* The synthesis is pure: it re-uses existing `FSRouteConfig` objects as
|
|
646
|
+
* source of truth, producing *new* objects with:
|
|
647
|
+
*
|
|
648
|
+
* - `pattern` : `/en/blog/:slug`
|
|
649
|
+
* - `id` : `en::<original-id>`
|
|
650
|
+
* - `module` : unchanged (same loader on disk)
|
|
651
|
+
* - everything else: shallow-copied
|
|
652
|
+
*
|
|
653
|
+
* Metadata routes (sitemap/robots/llms-txt/manifest) are NOT duplicated —
|
|
654
|
+
* they always sit at site root regardless of locale (same SEO rule as
|
|
655
|
+
* Next.js).
|
|
656
|
+
*
|
|
657
|
+
* The caller is responsible for passing the output through
|
|
658
|
+
* {@link sortRoutesByPriority} before writing the manifest.
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```ts
|
|
662
|
+
* const scan = await scanRoutes(rootDir);
|
|
663
|
+
* const prefixed = synthesizeLocaleRoutes(scan.routes, {
|
|
664
|
+
* locales: ["en", "ko"],
|
|
665
|
+
* defaultLocale: "en",
|
|
666
|
+
* });
|
|
667
|
+
* // scan.routes : [/, /blog, /blog/:slug]
|
|
668
|
+
* // prefixed : [/, /blog, /blog/:slug, /ko, /ko/blog, /ko/blog/:slug]
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
export function synthesizeLocaleRoutes(
|
|
672
|
+
routes: FSRouteConfig[],
|
|
673
|
+
options: LocaleSynthesisOptions
|
|
674
|
+
): FSRouteConfig[] {
|
|
675
|
+
const { locales, defaultLocale } = options;
|
|
676
|
+
if (!Array.isArray(locales) || locales.length === 0) return [...routes];
|
|
677
|
+
if (!locales.includes(defaultLocale)) {
|
|
678
|
+
throw new Error(
|
|
679
|
+
`[router] synthesizeLocaleRoutes: defaultLocale "${defaultLocale}" not in locales [${locales.join(", ")}]`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const out: FSRouteConfig[] = [];
|
|
684
|
+
for (const route of routes) {
|
|
685
|
+
// Metadata routes live at site root; never prefix them.
|
|
686
|
+
if (route.kind === "metadata") {
|
|
687
|
+
out.push(route);
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Default locale: unprefixed copy preserved verbatim (legacy +
|
|
692
|
+
// SEO neutral).
|
|
693
|
+
out.push(route);
|
|
694
|
+
|
|
695
|
+
for (const locale of locales) {
|
|
696
|
+
if (locale === defaultLocale) continue;
|
|
697
|
+
const prefixed = prefixRouteWithLocale(route, locale);
|
|
698
|
+
out.push(prefixed);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function prefixRouteWithLocale(route: FSRouteConfig, locale: string): FSRouteConfig {
|
|
705
|
+
const prefixedPattern = route.pattern === "/"
|
|
706
|
+
? `/${locale}`
|
|
707
|
+
: `/${locale}${route.pattern.startsWith("/") ? route.pattern : `/${route.pattern}`}`;
|
|
708
|
+
|
|
709
|
+
return {
|
|
710
|
+
...route,
|
|
711
|
+
id: `${locale}::${route.id}`,
|
|
712
|
+
pattern: prefixedPattern,
|
|
713
|
+
// `segments` is used for priority calculation + layout resolution;
|
|
714
|
+
// prepending a static locale segment keeps priority sensible and
|
|
715
|
+
// avoids collisions with real `[param]` segments.
|
|
716
|
+
segments: [
|
|
717
|
+
{ raw: locale, type: "static" },
|
|
718
|
+
...route.segments,
|
|
719
|
+
],
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
622
723
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
623
724
|
// Factory Function
|
|
624
725
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/src/router/index.ts
CHANGED
|
@@ -68,7 +68,13 @@ export {
|
|
|
68
68
|
} from "./fs-patterns";
|
|
69
69
|
|
|
70
70
|
// Scanner
|
|
71
|
-
export {
|
|
71
|
+
export {
|
|
72
|
+
FSScanner,
|
|
73
|
+
createFSScanner,
|
|
74
|
+
scanRoutes,
|
|
75
|
+
synthesizeLocaleRoutes,
|
|
76
|
+
type LocaleSynthesisOptions,
|
|
77
|
+
} from "./fs-scanner";
|
|
72
78
|
|
|
73
79
|
// Generator
|
|
74
80
|
export type { FSGenerateResult, GenerateOptions, RouteChangeCallback, FSRoutesWatcher } from "./fs-routes";
|