@pylonsync/sdk 0.3.246 → 0.3.248
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 +1 -1
- package/src/index.ts +54 -12
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -331,8 +331,11 @@ export interface RouteDefinition {
|
|
|
331
331
|
* NOT matched as navigable URLs — the host renders `not-found` for
|
|
332
332
|
* unmatched URLs (HTTP 404) and `error` on render failure (HTTP 500).
|
|
333
333
|
* `path` records the segment prefix the boundary covers (`/` for root).
|
|
334
|
+
* `"route"` is a form/method handler (`app/.../route.ts` exporting
|
|
335
|
+
* POST/PUT/PATCH/DELETE) — matched on its `path` for non-GET requests only,
|
|
336
|
+
* never rendered as a page.
|
|
334
337
|
*/
|
|
335
|
-
kind?: "page" | "not-found" | "error";
|
|
338
|
+
kind?: "page" | "not-found" | "error" | "route";
|
|
336
339
|
}
|
|
337
340
|
|
|
338
341
|
export function defineRoute(route: RouteDefinition): RouteDefinition {
|
|
@@ -506,8 +509,8 @@ export interface ManifestRoute {
|
|
|
506
509
|
auth?: string;
|
|
507
510
|
component?: string;
|
|
508
511
|
layouts?: string[];
|
|
509
|
-
/** "not-found" / "error"
|
|
510
|
-
kind?: "page" | "not-found" | "error";
|
|
512
|
+
/** "not-found" / "error" boundaries, or "route" form handlers; omitted for normal pages. */
|
|
513
|
+
kind?: "page" | "not-found" | "error" | "route";
|
|
511
514
|
}
|
|
512
515
|
|
|
513
516
|
export interface ManifestInputField {
|
|
@@ -669,8 +672,9 @@ export function routesToManifest(routes: RouteDefinition[]): ManifestRoute[] {
|
|
|
669
672
|
* ones at each depth — so the Rust matcher's first-match-wins
|
|
670
673
|
* lookup picks the right route.
|
|
671
674
|
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
675
|
+
* `not-found.tsx` / `error.tsx` boundaries are emitted as `kind`-tagged
|
|
676
|
+
* routes here; `loading.tsx` is resolved at render time by the SSR runtime
|
|
677
|
+
* (filesystem walk), so it needs no discovery entry.
|
|
674
678
|
*/
|
|
675
679
|
export async function discoverAppRoutes(opts?: {
|
|
676
680
|
appDir?: string;
|
|
@@ -719,8 +723,10 @@ export async function discoverAppRoutes(opts?: {
|
|
|
719
723
|
layouts: string[];
|
|
720
724
|
kind: "not-found" | "error";
|
|
721
725
|
};
|
|
726
|
+
type RouteHandlerHit = { segments: string[]; component: string };
|
|
722
727
|
const pages: PageHit[] = [];
|
|
723
728
|
const boundaries: BoundaryHit[] = [];
|
|
729
|
+
const routeHandlers: RouteHandlerHit[] = [];
|
|
724
730
|
|
|
725
731
|
// Resolve the first existing `<base>.{tsx,ts,jsx,js}` in `dir` and
|
|
726
732
|
// return it as a cwd-relative, extension-less module path (or null).
|
|
@@ -770,6 +776,12 @@ export async function discoverAppRoutes(opts?: {
|
|
|
770
776
|
kind: "error",
|
|
771
777
|
});
|
|
772
778
|
}
|
|
779
|
+
// Form/method handler (`route.ts` exporting POST/PUT/PATCH/DELETE). Matched
|
|
780
|
+
// on its path for non-GET requests only — never rendered, no layouts.
|
|
781
|
+
const routeHere = findModule(dir, "route");
|
|
782
|
+
if (routeHere) {
|
|
783
|
+
routeHandlers.push({ segments: [...segments], component: routeHere });
|
|
784
|
+
}
|
|
773
785
|
for (const e of entries) {
|
|
774
786
|
if (!e.isDirectory()) continue;
|
|
775
787
|
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
@@ -781,14 +793,27 @@ export async function discoverAppRoutes(opts?: {
|
|
|
781
793
|
}
|
|
782
794
|
walk(appDir, [], []);
|
|
783
795
|
|
|
796
|
+
// Segment kinds, least-to-most greedy:
|
|
797
|
+
// static "blog" rank 0 (most specific)
|
|
798
|
+
// dynamic param "[slug]" rank 1 → :slug
|
|
799
|
+
// catch-all "[...slug]" rank 2 → *slug (matches ≥1 segment)
|
|
800
|
+
// optional catch-all"[[...slug]]" rank 3 → *?slug (matches ≥0 segments)
|
|
801
|
+
// A catch-all is only valid as the LAST segment of a route (it consumes
|
|
802
|
+
// the rest of the path); Next.js enforces the same.
|
|
803
|
+
const isOptionalCatchAll = (s: string): boolean =>
|
|
804
|
+
s.startsWith("[[...") && s.endsWith("]]");
|
|
805
|
+
const isCatchAll = (s: string): boolean =>
|
|
806
|
+
s.startsWith("[...") && s.endsWith("]") && !isOptionalCatchAll(s);
|
|
784
807
|
const isParam = (s: string): boolean =>
|
|
785
|
-
s.startsWith("[") && s.endsWith("]");
|
|
808
|
+
s.startsWith("[") && s.endsWith("]") && !isCatchAll(s) && !isOptionalCatchAll(s);
|
|
809
|
+
const segRank = (s: string): number =>
|
|
810
|
+
isOptionalCatchAll(s) ? 3 : isCatchAll(s) ? 2 : isParam(s) ? 1 : 0;
|
|
786
811
|
pages.sort((a, b) => {
|
|
787
812
|
const minLen = Math.min(a.segments.length, b.segments.length);
|
|
788
813
|
for (let i = 0; i < minLen; i++) {
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
if (
|
|
814
|
+
const ar = segRank(a.segments[i]);
|
|
815
|
+
const br = segRank(b.segments[i]);
|
|
816
|
+
if (ar !== br) return ar - br; // more specific (lower rank) first
|
|
792
817
|
if (a.segments[i] !== b.segments[i]) {
|
|
793
818
|
return a.segments[i] < b.segments[i] ? -1 : 1;
|
|
794
819
|
}
|
|
@@ -796,9 +821,17 @@ export async function discoverAppRoutes(opts?: {
|
|
|
796
821
|
return a.segments.length - b.segments.length;
|
|
797
822
|
});
|
|
798
823
|
|
|
824
|
+
// Convert a discovered file segment to its route-pattern token. The Rust
|
|
825
|
+
// matcher (frontend.rs `match_ssr_route`) reads these markers back:
|
|
826
|
+
// :name dynamic param *name catch-all *?name optional catch-all
|
|
827
|
+
const segmentToToken = (s: string): string => {
|
|
828
|
+
if (isOptionalCatchAll(s)) return `*?${s.slice(5, -2)}`; // [[...x]] → *?x
|
|
829
|
+
if (isCatchAll(s)) return `*${s.slice(4, -1)}`; // [...x] → *x
|
|
830
|
+
if (isParam(s)) return `:${s.slice(1, -1)}`; // [x] → :x
|
|
831
|
+
return s;
|
|
832
|
+
};
|
|
799
833
|
const segmentsToPath = (segments: string[]): string =>
|
|
800
|
-
"/" +
|
|
801
|
-
segments.map((s) => (isParam(s) ? `:${s.slice(1, -1)}` : s)).join("/");
|
|
834
|
+
"/" + segments.map(segmentToToken).join("/");
|
|
802
835
|
|
|
803
836
|
const pageRoutes: RouteDefinition[] = pages.map((p) => ({
|
|
804
837
|
path: segmentsToPath(p.segments),
|
|
@@ -821,7 +854,16 @@ export async function discoverAppRoutes(opts?: {
|
|
|
821
854
|
kind: b.kind,
|
|
822
855
|
}));
|
|
823
856
|
|
|
824
|
-
|
|
857
|
+
// Form/method handlers. Keyed by the SAME path token form as pages
|
|
858
|
+
// (`:param` / `*catch-all`), matched by the host for non-GET methods only.
|
|
859
|
+
const routeRoutes: RouteDefinition[] = routeHandlers.map((r) => ({
|
|
860
|
+
path: segmentsToPath(r.segments),
|
|
861
|
+
mode: "ssr" as const,
|
|
862
|
+
component: r.component,
|
|
863
|
+
kind: "route" as const,
|
|
864
|
+
}));
|
|
865
|
+
|
|
866
|
+
return [...pageRoutes, ...boundaryRoutes, ...routeRoutes];
|
|
825
867
|
}
|
|
826
868
|
|
|
827
869
|
export function queriesToManifest(queries: QueryDefinition[]): ManifestQuery[] {
|