@mandujs/core 0.25.3 → 0.27.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.
@@ -53,9 +53,17 @@ export type LoaderConfig = z.infer<typeof LoaderConfig>;
53
53
 
54
54
  // ========== Route 설정 ==========
55
55
 
56
- export const RouteKind = z.enum(["page", "api"]);
56
+ export const RouteKind = z.enum(["page", "api", "metadata"]);
57
57
  export type RouteKind = z.infer<typeof RouteKind>;
58
58
 
59
+ /**
60
+ * Metadata route kinds — one for each file-convention metadata file
61
+ * detected under `app/`. Kept in sync with
62
+ * `@mandujs/core/routes` → `MetadataRouteKind`.
63
+ */
64
+ export const MetadataRouteKind = z.enum(["sitemap", "robots", "llms-txt", "manifest"]);
65
+ export type MetadataRouteKind = z.infer<typeof MetadataRouteKind>;
66
+
59
67
  export const SpecHttpMethod = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]);
60
68
  export type SpecHttpMethod = z.infer<typeof SpecHttpMethod>;
61
69
 
@@ -115,6 +123,27 @@ export const ApiRouteSpec = z.object({
115
123
 
116
124
  export type ApiRouteSpec = z.infer<typeof ApiRouteSpec>;
117
125
 
126
+ // ---- Metadata 라우트 (Issue #206) ----
127
+ // sitemap.ts / robots.ts / llms.txt.ts / manifest.ts — file-convention
128
+ // metadata routes. Dispatched through `@mandujs/core/routes`.
129
+ export const MetadataRouteSpec = z.object({
130
+ ...RouteSpecBase,
131
+ kind: z.literal("metadata"),
132
+ /** Which metadata file this entry represents. */
133
+ metadataKind: MetadataRouteKind,
134
+ /** MIME type used on the served response. */
135
+ contentType: z.string().min(1),
136
+ // Re-declare shared optional fields so downstream consumers that
137
+ // switch on kind can still read them without type widening gymnastics.
138
+ componentModule: z.string().optional(),
139
+ methods: z.array(SpecHttpMethod).optional(),
140
+ layoutChain: z.array(z.string()).optional(),
141
+ loadingModule: z.string().optional(),
142
+ errorModule: z.string().optional(),
143
+ });
144
+
145
+ export type MetadataRouteSpec = z.infer<typeof MetadataRouteSpec>;
146
+
118
147
  // ---- discriminatedUnion ----
119
148
  export const RouteSpec = z.discriminatedUnion("kind", [
120
149
  // PageRouteSpec에 .refine()이 적용되어 있으므로 내부 shape를 직접 사용
@@ -136,6 +165,17 @@ export const RouteSpec = z.discriminatedUnion("kind", [
136
165
  loadingModule: z.string().optional(),
137
166
  errorModule: z.string().optional(),
138
167
  }),
168
+ z.object({
169
+ ...RouteSpecBase,
170
+ kind: z.literal("metadata"),
171
+ metadataKind: MetadataRouteKind,
172
+ contentType: z.string().min(1),
173
+ componentModule: z.string().optional(),
174
+ methods: z.array(SpecHttpMethod).optional(),
175
+ layoutChain: z.array(z.string()).optional(),
176
+ loadingModule: z.string().optional(),
177
+ errorModule: z.string().optional(),
178
+ }),
139
179
  ]);
140
180
 
141
181
  export type RouteSpec = z.infer<typeof RouteSpec>;
@@ -194,6 +234,16 @@ export function assertApiRoute(route: RouteSpec): asserts route is ApiRouteSpec
194
234
  }
195
235
  }
196
236
 
237
+ /**
238
+ * Asserts that the given route is a metadata route.
239
+ * After this call, TypeScript narrows the type to MetadataRouteSpec.
240
+ */
241
+ export function assertMetadataRoute(route: RouteSpec): asserts route is MetadataRouteSpec {
242
+ if (route.kind !== "metadata") {
243
+ throw new Error(`Expected metadata route, got "${route.kind}" (id: ${route.id})`);
244
+ }
245
+ }
246
+
197
247
  // ========== 유틸리티 함수 ==========
198
248
 
199
249
  /**