@kosmojs/dev 0.4.2 → 0.4.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.
- package/package.json +4 -3
- package/pkg/chassis.js +308 -1
- package/pkg/chassis.js.map +1 -1
- package/pkg/index.d.ts +2 -1
- package/pkg/index.js +1722 -879
- package/pkg/index.js.map +1 -1
- package/pkg/preview.d.ts +15 -0
- package/pkg/runner.d.ts +22 -0
- package/pkg/templates/run.d.ts +29 -0
- package/pkg/assets/pkg-Bkag325q.js +0 -511
- package/pkg/assets/pkg-Bkag325q.js.map +0 -1
package/pkg/index.js
CHANGED
|
@@ -1,6 +1,588 @@
|
|
|
1
|
-
import{
|
|
2
|
-
|
|
3
|
-
`,
|
|
1
|
+
import{dirname as e,join as t,posix as n,resolve as r}from"node:path";import{styleText as i}from"node:util";import{DEFAULT_APIBASE as a,RequestBodyTargets as o,RequestValidationTargets as s,createRouteResolver as c,createTemplateResolver as l,defaults as u}from"@kosmojs/core";import{collectVirtualModules as d,createH3Pattern as f,createHonoPattern as p,createPathPattern as m,createWatchedApiRouteEntriesFilter as h,createWatchedPageRouteEntriesFilter as g,defineGenerator as _,defineGeneratorFactory as v,mergeConfigs as y,nestedRoutesFactory as b,pathExists as x,pathResolver as S,pathTokensFactory as C,renderFactory as w,renderToFile as T,sortRoutes as E,sortRoutesForResolution as D,spinnerFactory as ee,vitePlugins as O}from"@kosmojs/lib";import k from"semver";import{routeRenderHelpers as A}from"@kosmojs/core/generators";import{HTTPMethods as te}from"@kosmojs/core/fetch";import j from"crc/crc32";import ne from"@mdx-js/rollup";import{build as M,createFilter as re}from"vite";import ie from"yaml";import{parse as ae}from"path-to-regexp";import oe from"typebox";import se from"@vitejs/plugin-react";import N from"vite-plugin-solid";import{access as ce,constants as le,cp as P,mkdir as ue,rm as F,writeFile as de}from"node:fs/promises";import{svelte as fe}from"@sveltejs/vite-plugin-svelte";import pe from"@vitejs/plugin-vue";var me={type:`module`,private:!0,name:`@kosmojs/core-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,semver:`^7.8.5`},devDependencies:{"@types/semver":`^7.8.0`,"path-to-regexp":`^8.4.2`}},he=`export const base = "{{config.base}}";
|
|
2
|
+
export const apiBase = "{{config.apiBase}}";
|
|
3
|
+
`,I=`import type { StaticParams } from "./types";
|
|
4
|
+
|
|
5
|
+
export * from "./config";
|
|
6
|
+
export * from "./routes";
|
|
7
|
+
export * from "./types";
|
|
8
|
+
|
|
9
|
+
export const defineStaticParams = <T extends keyof StaticParams>(
|
|
10
|
+
variants: Array<StaticParams[T]>,
|
|
11
|
+
) => {
|
|
12
|
+
return variants
|
|
13
|
+
};
|
|
14
|
+
`,L=`{{> routeMapperPartial}}
|
|
15
|
+
|
|
16
|
+
import { base, apiBase } from "./config";
|
|
17
|
+
|
|
18
|
+
// used by backend generators and fetch clients
|
|
19
|
+
export const apiRouteMap = {
|
|
20
|
+
{{#each apiRoutes}}
|
|
21
|
+
"{{name}}": apiRouteMapper<[{{serializeParamsTupleElements .}}]>(
|
|
22
|
+
join(base, apiBase),
|
|
23
|
+
{{serializeApiRoute .}},
|
|
24
|
+
),
|
|
25
|
+
{{/each}}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// used by frontend generators on Link component
|
|
29
|
+
export const pageRouteMap = {
|
|
30
|
+
{{#each pageRoutes}}
|
|
31
|
+
"{{name}}": pageRouteMapper<[{{serializeParamsTupleElements .}}]>(
|
|
32
|
+
base,
|
|
33
|
+
{{serializePageRoute .}},
|
|
34
|
+
),
|
|
35
|
+
{{/each}}
|
|
36
|
+
};
|
|
37
|
+
`,R=`import { compile, match } from "path-to-regexp";
|
|
38
|
+
|
|
39
|
+
import {
|
|
40
|
+
type ApiRouteSerialized,
|
|
41
|
+
type PageRouteSerialized,
|
|
42
|
+
stringifySearchParams,
|
|
43
|
+
type ValidationTarget,
|
|
44
|
+
} from "@kosmojs/core";
|
|
45
|
+
import { createHost, join } from "@kosmojs/core/fetch";
|
|
46
|
+
import type { RoutePathMethods } from "@kosmojs/core/generators";
|
|
47
|
+
|
|
48
|
+
type NormalizeParams = (path: string) => Record<string, unknown>;
|
|
49
|
+
|
|
50
|
+
type NormalizeSearchParams = (
|
|
51
|
+
searchParams: Record<string, unknown>,
|
|
52
|
+
method: string,
|
|
53
|
+
) => Record<string, unknown>;
|
|
54
|
+
|
|
55
|
+
type PayloadResolver = <T>(
|
|
56
|
+
payload: Record<ValidationTarget, T> | undefined,
|
|
57
|
+
target: ValidationTarget,
|
|
58
|
+
method: string,
|
|
59
|
+
) => T | Record<string, unknown> | undefined;
|
|
60
|
+
|
|
61
|
+
export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
62
|
+
base: string,
|
|
63
|
+
{
|
|
64
|
+
name,
|
|
65
|
+
pathPattern,
|
|
66
|
+
params,
|
|
67
|
+
numericProperties,
|
|
68
|
+
booleanProperties,
|
|
69
|
+
}: ApiRouteSerialized,
|
|
70
|
+
): RoutePathMethods<ParamsT> & {
|
|
71
|
+
normalizeParams: NormalizeParams;
|
|
72
|
+
normalizeSearchParams: NormalizeSearchParams;
|
|
73
|
+
payloadResolver: PayloadResolver;
|
|
74
|
+
} => {
|
|
75
|
+
const toPath = compile(pathPattern);
|
|
76
|
+
const pathMatcher = match(join(base, pathPattern));
|
|
77
|
+
|
|
78
|
+
const maybeNumber = (val: unknown) => {
|
|
79
|
+
if (val === undefined || val === null) {
|
|
80
|
+
return val;
|
|
81
|
+
}
|
|
82
|
+
const n = Number(val);
|
|
83
|
+
return Number.isFinite(n) ? n : val;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const maybeBoolean = (val: unknown) => {
|
|
87
|
+
return [true, false, "true", "false"].includes(val as never) //
|
|
88
|
+
? JSON.parse(val as never)
|
|
89
|
+
: val;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const resolveParam = (path: string, param: (typeof params)[number]) => {
|
|
93
|
+
try {
|
|
94
|
+
const match = pathMatcher(path);
|
|
95
|
+
return match ? match.params[param] : undefined;
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const normalizeSearchParams: NormalizeSearchParams = (
|
|
102
|
+
searchParams,
|
|
103
|
+
method,
|
|
104
|
+
) => {
|
|
105
|
+
return Object.fromEntries(
|
|
106
|
+
Object.entries(searchParams).map(([k, v]) => {
|
|
107
|
+
if (numericProperties.query?.[method]?.includes(k)) {
|
|
108
|
+
return [
|
|
109
|
+
k,
|
|
110
|
+
Array.isArray(v) ? v.map((e) => maybeNumber(e)) : maybeNumber(v),
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
if (booleanProperties.query?.[method]?.includes(k)) {
|
|
114
|
+
return [
|
|
115
|
+
k,
|
|
116
|
+
Array.isArray(v) ? v.map((e) => maybeBoolean(e)) : maybeBoolean(v),
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
return [k, v];
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const normalizeParams: NormalizeParams = (path) => {
|
|
125
|
+
return params.reduce((map: Record<string, unknown>, param) => {
|
|
126
|
+
const value = resolveParam(path, param);
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
map[param] = numericProperties.params.includes(param)
|
|
129
|
+
? value.map((e) => maybeNumber(e))
|
|
130
|
+
: value;
|
|
131
|
+
} else if (value) {
|
|
132
|
+
map[param] = numericProperties.params.includes(param)
|
|
133
|
+
? maybeNumber(value)
|
|
134
|
+
: value;
|
|
135
|
+
}
|
|
136
|
+
return map;
|
|
137
|
+
}, {});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const paramsMapper: RoutePathMethods<ParamsT>["paramsMapper"] = (
|
|
141
|
+
input,
|
|
142
|
+
opt,
|
|
143
|
+
) => {
|
|
144
|
+
return params.reduce<Record<string, unknown>>((map, name, i) => {
|
|
145
|
+
const coerceNumbers = opt?.coerceNumbers
|
|
146
|
+
? numericProperties.params.includes(name)
|
|
147
|
+
: false;
|
|
148
|
+
if (Array.isArray(input[i])) {
|
|
149
|
+
map[name] = coerceNumbers
|
|
150
|
+
? input[i].map((v) => maybeNumber(v))
|
|
151
|
+
: input[i].map(String);
|
|
152
|
+
} else if (input[i] !== undefined) {
|
|
153
|
+
map[name] = coerceNumbers ? maybeNumber(input[i]) : String(input[i]);
|
|
154
|
+
}
|
|
155
|
+
return map;
|
|
156
|
+
}, {});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const parametrize: RoutePathMethods<ParamsT>["parametrize"] = (params) => {
|
|
160
|
+
try {
|
|
161
|
+
return toPath(paramsMapper(params) as never);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(\`❗ERROR: Failed building path for \${name}\`);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const path: RoutePathMethods<ParamsT>["path"] = (params, query, opt) => {
|
|
169
|
+
const path = join(
|
|
170
|
+
opt?.prefix === false
|
|
171
|
+
? "/"
|
|
172
|
+
: typeof opt?.prefix === "string"
|
|
173
|
+
? opt.prefix
|
|
174
|
+
: base,
|
|
175
|
+
parametrize(params),
|
|
176
|
+
);
|
|
177
|
+
return query //
|
|
178
|
+
? [path, stringifySearchParams(query)].join("?")
|
|
179
|
+
: path;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const href: RoutePathMethods<ParamsT>["href"] = (
|
|
183
|
+
host,
|
|
184
|
+
params,
|
|
185
|
+
query,
|
|
186
|
+
opt,
|
|
187
|
+
) => {
|
|
188
|
+
return createHost(host) + path(params, query, opt);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const payloadResolver: PayloadResolver = (payload, target, method) => {
|
|
192
|
+
const data = payload?.[target];
|
|
193
|
+
|
|
194
|
+
if (target === "query") {
|
|
195
|
+
return Object.fromEntries(
|
|
196
|
+
Object.entries({ ...data }).map(([k, v]) => {
|
|
197
|
+
return [
|
|
198
|
+
k,
|
|
199
|
+
numericProperties.query[method]?.includes(k)
|
|
200
|
+
? Array.isArray(v)
|
|
201
|
+
? v.map((v) => maybeNumber(v))
|
|
202
|
+
: maybeNumber(v)
|
|
203
|
+
: v,
|
|
204
|
+
];
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (data instanceof FormData) {
|
|
210
|
+
return [...data].reduce<
|
|
211
|
+
Record<string, FormDataEntryValue | Array<FormDataEntryValue>>
|
|
212
|
+
>((map, [key, val]) => {
|
|
213
|
+
if (key in map) {
|
|
214
|
+
map[key] = [map[key]].flat().concat(val);
|
|
215
|
+
} else {
|
|
216
|
+
map[key] = val;
|
|
217
|
+
}
|
|
218
|
+
return map;
|
|
219
|
+
}, {});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return data;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
normalizeParams,
|
|
227
|
+
normalizeSearchParams,
|
|
228
|
+
payloadResolver,
|
|
229
|
+
paramsMapper,
|
|
230
|
+
parametrize,
|
|
231
|
+
path,
|
|
232
|
+
href,
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const pageRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
237
|
+
base: string,
|
|
238
|
+
route: PageRouteSerialized,
|
|
239
|
+
): RoutePathMethods<ParamsT> => {
|
|
240
|
+
const toPath = compile(route.pathPattern);
|
|
241
|
+
|
|
242
|
+
const paramsMapper: RoutePathMethods<ParamsT>["paramsMapper"] = (params) => {
|
|
243
|
+
return route.params.reduce<Record<string, unknown>>((map, name, i) => {
|
|
244
|
+
if (Array.isArray(params[i])) {
|
|
245
|
+
map[name] = params[i].map(String);
|
|
246
|
+
} else if (params[i] !== undefined) {
|
|
247
|
+
map[name] = String(params[i]);
|
|
248
|
+
}
|
|
249
|
+
return map;
|
|
250
|
+
}, {});
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const parametrize: RoutePathMethods<ParamsT>["parametrize"] = (params) => {
|
|
254
|
+
try {
|
|
255
|
+
return toPath(paramsMapper(params) as never);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(\`❗ERROR: Failed building path for \${route.name}\`);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const path: RoutePathMethods<ParamsT>["path"] = (params, query, opt) => {
|
|
263
|
+
const path = join(
|
|
264
|
+
opt?.prefix === false
|
|
265
|
+
? "/"
|
|
266
|
+
: typeof opt?.prefix === "string"
|
|
267
|
+
? opt.prefix
|
|
268
|
+
: base,
|
|
269
|
+
parametrize(params),
|
|
270
|
+
);
|
|
271
|
+
return query //
|
|
272
|
+
? [path, stringifySearchParams(query)].join("?")
|
|
273
|
+
: path;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const href: RoutePathMethods<ParamsT>["href"] = (
|
|
277
|
+
host,
|
|
278
|
+
params,
|
|
279
|
+
query,
|
|
280
|
+
opt,
|
|
281
|
+
) => {
|
|
282
|
+
return createHost(host) + path(params, query, opt);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
paramsMapper,
|
|
287
|
+
parametrize,
|
|
288
|
+
path,
|
|
289
|
+
href,
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
`,ge=`import { AsyncLocalStorage } from "node:async_hooks";
|
|
293
|
+
|
|
294
|
+
export type RequestContext = {
|
|
295
|
+
headers?: HeadersInit;
|
|
296
|
+
tsqClient?: unknown;
|
|
297
|
+
error?: unknown;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Request-scoped context store.
|
|
302
|
+
* */
|
|
303
|
+
export const store = new AsyncLocalStorage<RequestContext>();
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Origin used to absolutize the relative URLs the client produces.
|
|
307
|
+
* */
|
|
308
|
+
export const ssrOrigin = "http://ssr.local";
|
|
309
|
+
|
|
310
|
+
export const redirectCodes = [
|
|
311
|
+
// Moved Permanently
|
|
312
|
+
301,
|
|
313
|
+
// Found (temporary)
|
|
314
|
+
302,
|
|
315
|
+
// See Other (redirect after POST)
|
|
316
|
+
303,
|
|
317
|
+
// Temporary Redirect (preserves method)
|
|
318
|
+
307,
|
|
319
|
+
// Permanent Redirect (preserves method)
|
|
320
|
+
308,
|
|
321
|
+
];
|
|
322
|
+
`,_e=`export type Override<A, B> = Omit<A, keyof B> & B;
|
|
323
|
+
|
|
324
|
+
export type StaticParams = {
|
|
325
|
+
{{#each pageRoutes}}
|
|
326
|
+
"{{name}}": [ {{serializeParamsTupleElements .}} ];
|
|
327
|
+
{{/each}}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
{{#if pageRoutes.length}}
|
|
331
|
+
export type LinkProps =
|
|
332
|
+
{{#each pageRoutes}}
|
|
333
|
+
| [ "{{name}}", {{serializeParamsTupleElements .}} ]
|
|
334
|
+
{{/each}};
|
|
335
|
+
{{else}}
|
|
336
|
+
export type LinkProps = never;
|
|
337
|
+
{{/if}}
|
|
338
|
+
`,ve=`declare module "virtual:kosmo/env" {
|
|
339
|
+
export const command: "serve" | "build";
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
declare module "virtual:kosmo/backend-app" {
|
|
343
|
+
import type { FetchApp, NodeApp } from "@kosmojs/core";
|
|
344
|
+
const backend: FetchApp | NodeApp | undefined;
|
|
345
|
+
export default backend;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
declare module "virtual:kosmo/fetch-transport" {
|
|
349
|
+
import type { Transport } from "@kosmojs/core/fetch";
|
|
350
|
+
/**
|
|
351
|
+
* Undefined on the client, where fetch clients fall back to global fetch.
|
|
352
|
+
* On the SSR bundle it dispatches straight into the backend app, in process.
|
|
353
|
+
* Supplied by the \`kosmo:virtualModules\` Vite plugin - there is no file.
|
|
354
|
+
* */
|
|
355
|
+
export const transport: Transport | undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Enhances base TypeScript types with JSON Schema validation constraints.
|
|
360
|
+
* Allows declaring refined types that carry validation metadata for runtime
|
|
361
|
+
* schema validation while maintaining full TypeScript type safety.
|
|
362
|
+
*
|
|
363
|
+
* Useful for generating validation schemas and ensuring
|
|
364
|
+
* data conforms to specific business rules beyond basic type checking.
|
|
365
|
+
* */
|
|
366
|
+
declare type VRefine<
|
|
367
|
+
T extends unknown[] | number | string | object,
|
|
368
|
+
_ extends T extends unknown[]
|
|
369
|
+
? TArrayOptions
|
|
370
|
+
: T extends number
|
|
371
|
+
? TNumberOptions
|
|
372
|
+
: T extends string
|
|
373
|
+
? TStringOptions
|
|
374
|
+
: TObjectOptions,
|
|
375
|
+
> = T;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Type definitions inspired by and gently adapted from TypeBox.
|
|
379
|
+
* Original TypeBox created by sinclairzx81: https://github.com/sinclairzx81/typebox
|
|
380
|
+
* TypeBox is licensed under MIT: https://github.com/sinclairzx81/typebox/blob/main/license
|
|
381
|
+
*
|
|
382
|
+
* These types provide JSON Schema compatible type refinements for TypeScript.
|
|
383
|
+
* */
|
|
384
|
+
interface TSchema {}
|
|
385
|
+
|
|
386
|
+
// ------------------------------------------------------------------
|
|
387
|
+
// ObjectOptions
|
|
388
|
+
// ------------------------------------------------------------------
|
|
389
|
+
interface TObjectOptions {
|
|
390
|
+
/**
|
|
391
|
+
* Defines whether additional properties are allowed beyond those explicitly defined in \`properties\`.
|
|
392
|
+
*/
|
|
393
|
+
additionalProperties?: TSchema | boolean;
|
|
394
|
+
/**
|
|
395
|
+
* The minimum number of properties required in the object.
|
|
396
|
+
*/
|
|
397
|
+
minProperties?: number;
|
|
398
|
+
/**
|
|
399
|
+
* The maximum number of properties allowed in the object.
|
|
400
|
+
*/
|
|
401
|
+
maxProperties?: number;
|
|
402
|
+
/**
|
|
403
|
+
* Defines conditional requirements for properties.
|
|
404
|
+
*/
|
|
405
|
+
dependencies?: Record<string, boolean | TSchema | string[]>;
|
|
406
|
+
/**
|
|
407
|
+
* Specifies properties that *must* be present if a given property is present.
|
|
408
|
+
*/
|
|
409
|
+
dependentRequired?: Record<string, string[]>;
|
|
410
|
+
/**
|
|
411
|
+
* Defines schemas that apply if a specific property is present.
|
|
412
|
+
*/
|
|
413
|
+
dependentSchemas?: Record<string, TSchema>;
|
|
414
|
+
/**
|
|
415
|
+
* Maps regular expressions to schemas properties matching a pattern must validate against the schema.
|
|
416
|
+
*/
|
|
417
|
+
patternProperties?: Record<string, TSchema>;
|
|
418
|
+
/**
|
|
419
|
+
* A schema that all property names within the object must validate against.
|
|
420
|
+
*/
|
|
421
|
+
propertyNames?: TSchema;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ------------------------------------------------------------------
|
|
425
|
+
// ArrayOptions
|
|
426
|
+
// ------------------------------------------------------------------
|
|
427
|
+
interface TArrayOptions {
|
|
428
|
+
/**
|
|
429
|
+
* The minimum number of items allowed in the array.
|
|
430
|
+
*/
|
|
431
|
+
minItems?: number;
|
|
432
|
+
/**
|
|
433
|
+
* The maximum number of items allowed in the array.
|
|
434
|
+
*/
|
|
435
|
+
maxItems?: number;
|
|
436
|
+
/**
|
|
437
|
+
* A schema that at least one item in the array must validate against.
|
|
438
|
+
*/
|
|
439
|
+
contains?: TSchema;
|
|
440
|
+
/**
|
|
441
|
+
* The minimum number of array items that must validate against the \`contains\` schema.
|
|
442
|
+
*/
|
|
443
|
+
minContains?: number;
|
|
444
|
+
/**
|
|
445
|
+
* The maximum number of array items that may validate against the \`contains\` schema.
|
|
446
|
+
*/
|
|
447
|
+
maxContains?: number;
|
|
448
|
+
/**
|
|
449
|
+
* An array of schemas, where each schema in \`prefixItems\` validates against items at corresponding positions from the beginning of the array.
|
|
450
|
+
*/
|
|
451
|
+
prefixItems?: TSchema[];
|
|
452
|
+
/**
|
|
453
|
+
* If \`true\`, all items in the array must be unique.
|
|
454
|
+
*/
|
|
455
|
+
uniqueItems?: boolean;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ------------------------------------------------------------------
|
|
459
|
+
// NumberOptions
|
|
460
|
+
// ------------------------------------------------------------------
|
|
461
|
+
interface TNumberOptions {
|
|
462
|
+
/**
|
|
463
|
+
* Specifies an exclusive upper limit for the number (number must be less than this value).
|
|
464
|
+
*/
|
|
465
|
+
exclusiveMaximum?: number | bigint;
|
|
466
|
+
/**
|
|
467
|
+
* Specifies an exclusive lower limit for the number (number must be greater than this value).
|
|
468
|
+
*/
|
|
469
|
+
exclusiveMinimum?: number | bigint;
|
|
470
|
+
/**
|
|
471
|
+
* Specifies an inclusive upper limit for the number (number must be less than or equal to this value).
|
|
472
|
+
*/
|
|
473
|
+
maximum?: number | bigint;
|
|
474
|
+
/**
|
|
475
|
+
* Specifies an inclusive lower limit for the number (number must be greater than or equal to this value).
|
|
476
|
+
*/
|
|
477
|
+
minimum?: number | bigint;
|
|
478
|
+
/**
|
|
479
|
+
* Specifies that the number must be a multiple of this value.
|
|
480
|
+
*/
|
|
481
|
+
multipleOf?: number | bigint;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ------------------------------------------------------------------
|
|
485
|
+
// StringOptions
|
|
486
|
+
// ------------------------------------------------------------------
|
|
487
|
+
type TFormat =
|
|
488
|
+
| "date-time"
|
|
489
|
+
| "date"
|
|
490
|
+
| "duration"
|
|
491
|
+
| "email"
|
|
492
|
+
| "hostname"
|
|
493
|
+
| "idn-email"
|
|
494
|
+
| "idn-hostname"
|
|
495
|
+
| "ipv4"
|
|
496
|
+
| "ipv6"
|
|
497
|
+
| "iri-reference"
|
|
498
|
+
| "iri"
|
|
499
|
+
| "json-pointer-uri-fragment"
|
|
500
|
+
| "json-pointer"
|
|
501
|
+
| "json-string"
|
|
502
|
+
| "regex"
|
|
503
|
+
| "relative-json-pointer"
|
|
504
|
+
| "time"
|
|
505
|
+
| "uri-reference"
|
|
506
|
+
| "uri-template"
|
|
507
|
+
| "url"
|
|
508
|
+
| "uuid";
|
|
509
|
+
|
|
510
|
+
interface TStringOptions {
|
|
511
|
+
/**
|
|
512
|
+
* Specifies the expected string format.
|
|
513
|
+
*
|
|
514
|
+
* Common values include:
|
|
515
|
+
* - \`base64\` – Base64-encoded string.
|
|
516
|
+
* - \`date-time\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time format.
|
|
517
|
+
* - \`date\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date (YYYY-MM-DD).
|
|
518
|
+
* - \`duration\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) duration format.
|
|
519
|
+
* - \`email\` – RFC 5321/5322 compliant email address.
|
|
520
|
+
* - \`hostname\` – RFC 1034/1035 compliant host name.
|
|
521
|
+
* - \`idn-email\` – Internationalized email address.
|
|
522
|
+
* - \`idn-hostname\` – Internationalized host name.
|
|
523
|
+
* - \`ipv4\` – IPv4 address.
|
|
524
|
+
* - \`ipv6\` – IPv6 address.
|
|
525
|
+
* - \`iri\` / \`iri-reference\` – Internationalized Resource Identifier.
|
|
526
|
+
* - \`json-pointer\` / \`json-pointer-uri-fragment\` – JSON Pointer format.
|
|
527
|
+
* - \`json-string\` – String containing valid JSON.
|
|
528
|
+
* - \`regex\` – Regular expression syntax.
|
|
529
|
+
* - \`relative-json-pointer\` – Relative JSON Pointer format.
|
|
530
|
+
* - \`time\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time (HH:MM:SS).
|
|
531
|
+
* - \`uri-reference\` / \`uri-template\` – URI reference or template.
|
|
532
|
+
* - \`url\` – Web URL format.
|
|
533
|
+
* - \`uuid\` – RFC 4122 UUID string.
|
|
534
|
+
*
|
|
535
|
+
* May also be a custom format string.
|
|
536
|
+
*/
|
|
537
|
+
format?: TFormat;
|
|
538
|
+
/**
|
|
539
|
+
* Specifies the minimum number of characters allowed in the string.
|
|
540
|
+
* Must be a non-negative integer.
|
|
541
|
+
*/
|
|
542
|
+
minLength?: number;
|
|
543
|
+
/**
|
|
544
|
+
* Specifies the maximum number of characters allowed in the string.
|
|
545
|
+
* Must be a non-negative integer.
|
|
546
|
+
*/
|
|
547
|
+
maxLength?: number;
|
|
548
|
+
/**
|
|
549
|
+
* Specifies a regular expression pattern that the string value must match.
|
|
550
|
+
* Can be provided as a string (ECMA-262 regex syntax) or a \`RegExp\` object.
|
|
551
|
+
*/
|
|
552
|
+
pattern?: string | RegExp;
|
|
553
|
+
}
|
|
554
|
+
`,ye=`# Ignore all files
|
|
555
|
+
*
|
|
556
|
+
|
|
557
|
+
# But don't ignore directories (so Git can traverse them)
|
|
558
|
+
!*/
|
|
559
|
+
|
|
560
|
+
# And don't ignore these files at any depth
|
|
561
|
+
!cache.json
|
|
562
|
+
!types.ts
|
|
563
|
+
`,be=`export declare global {
|
|
564
|
+
interface Window {
|
|
565
|
+
__KOSMO_HYDRATION_BOOL__: boolean;
|
|
566
|
+
__KOSMO_HYDRATION_DATA__: Record<string, unknown> | undefined;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
`,xe=`<!doctype html>
|
|
570
|
+
<html lang="en">
|
|
571
|
+
<head>
|
|
572
|
+
<meta charset="UTF-8" />
|
|
573
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
574
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
575
|
+
</head>
|
|
576
|
+
<body>
|
|
577
|
+
<div id="app"><!--app-html--></div>
|
|
578
|
+
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
579
|
+
</body>
|
|
580
|
+
</html>
|
|
581
|
+
`,Se=`// stub schemas, specialized generators supposed to overwrite this file
|
|
582
|
+
import type { ValidationSchemas } from "@kosmojs/core";
|
|
583
|
+
export type { ValidationSchemas };
|
|
584
|
+
export const validationSchemas: ValidationSchemas = {};
|
|
585
|
+
`,z=({dependencies:e,devDependencies:t},n)=>{let r="${configDir}",i=Object.keys({...e,...t}),a={types:[`vite/client`,...[`node`,`deno`,`bun`].flatMap(e=>i.includes(`@types/${e}`)?[`@types/${e}`]:[])],moduleResolution:`bundler`,module:`ESNext`,target:`ESNext`,strict:!0,exactOptionalPropertyTypes:!0,noImplicitAny:!0,noImplicitThis:!0,noImplicitOverride:!0,noImplicitReturns:!0,noUnusedLocals:!1,noUnusedParameters:!1,allowArbitraryExtensions:!0,allowImportingTsExtensions:!0,allowUnreachableCode:!1,allowUnusedLabels:!1,useUnknownInCatchVariables:!0,noFallthroughCasesInSwitch:!0,noUncheckedSideEffectImports:!0,resolveJsonModule:!0,esModuleInterop:!0,verbatimModuleSyntax:!0,skipLibCheck:!0,noEmit:!0};return n?{include:[`${r}/`,`${r}/../../${u.libDir}/${n}/`,`${r}/../../**/*.d.ts`],compilerOptions:{...a,types:[...a.types],paths:{[`${u.appPrefix}/*`]:[`${r}/../../*`],[`${u.srcPrefix}/*`]:[`${r}/*`],[`${u.libPrefix}/*`]:[`${r}/../../${u.libDir}/${n}/*`]}}}:{include:[`${r}/`],exclude:[`${r}/${u.srcDir}/`],compilerOptions:{...a,paths:{[`${u.appPrefix}/*`]:[`${r}/*`]}}}},Ce=v(t=>{let{createPath:n,createImportHelpers:a}=S(t),{generators:o}=t.config,s=async()=>{let{dependencies:e={},devDependencies:a={}}=await import(r(t.root,`package.json`),{with:{type:`json`}}).then(e=>e.default);{let t=[],n=[],r=o.flatMap(e=>[`dependencies`,`devDependencies`].flatMap(t=>e[t]?Object.entries(typeof e[t]==`function`?e[t](e.options):e[t]).flatMap(([e,n])=>{let r=k.minVersion(n)?.version;return r?[[e,r,t]]:[]}):[]));for(let[i,o,s]of r){let r=e[i]||a[i],c=r?k.minVersion(r)?.version:void 0;!r||!c?t.push([i,o,s]):k.lt(c,o)&&n.push([i,o,s])}if(t.length){console.error(i([`red`,`italic`],`There are ${t.length} missing dependencies, please consider installing them.`));for(let e of[`dependencies`,`devDependencies`]){let n=t.filter(t=>t[2]===e);n.length&&console.error(`${e}: ${i([`blue`],n.map(([e])=>e).join(` `))}`)}}n.length&&(console.error(i([`yellow`,`italic`],`There are ${n.length} outdated dependencies, please consider updating them:`)),console.error(n.map(([e])=>e).join(` `)),console.error())}{await T(r(t.root,`tsconfig.json`),JSON.stringify({extends:`./${u.libDir}/tsconfig.json`},void 0,2),{},{overwrite:!1}),await T(n.lib(`../tsconfig.json`),JSON.stringify(z({dependencies:e,devDependencies:a}),void 0,2),{}),await T(n.src(`tsconfig.json`),JSON.stringify({extends:`../../${u.libDir}/${t.name}/tsconfig.json`},void 0,2),{},{overwrite:!1});let i=z({dependencies:e,devDependencies:a},t.name),s={},c=new Set(i.compilerOptions.types||[]);for(let{meta:e}of o){e.jsx&&(s.jsx=e.jsx),e.jsxImportSource&&(s.jsxImportSource=e.jsxImportSource);for(let t of e.types||[])c.add(t)}await T(n.lib(`tsconfig.json`),JSON.stringify({...i,compilerOptions:{...i.compilerOptions,...s,types:[...c.values()]}},void 0,2),{})}for(let[e,t]of[[`env.d.ts`,ve],[`global.d.ts`,be]])await T(n.lib(`../${e}`),t,{});await T(n.lib(`../.gitignore`),ye,{},{overwrite:!1}),o.some(e=>e.meta.slot===`frontend`)&&await T(n.src(`index.html`),xe,{entryDir:u.entryDir},{overwrite:e=>!e?.trim()})},c=async r=>{let{renderToFile:i}=w({helpers:{...a({origin:`lib`}),...A()},partials:{routeMapperPartial:R}}),o=r.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]),s=r.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]);await i(n.libCore(`routes.ts`),L,{apiRoutes:o,pageRoutes:s});for(let[e,r]of[[`config.ts`,he],[`types.ts`,_e],[`ssr.ts`,ge],[`index.ts`,I]])await i(n.libCore(e),r,{...t,apiRoutes:o,pageRoutes:s});for(let{kind:t,entry:a}of r)t===`apiRoute`&&await i(n.libApi(e(a.file),`schemas.ts`),Se,{route:a},{overwrite:!1})};return{start:s,watch:c,build:c,virtualModules(){let{createImport:e}=S(t);return[{specifier:`virtual:kosmo/backend-app`,csr:`export default undefined;`,ssr:o.some(e=>e.meta.slot===`backend`)?`export { default } from "${e.api([`app`],{origin:`lib`})}";`:`export default undefined;`}]}}}),B=_({meta:{name:`Core`},dependencies:{"path-to-regexp":me.devDependencies[`path-to-regexp`]},factory:Ce}),we={type:`module`,private:!0,name:`@kosmojs/fetch-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/fetch-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`},devDependencies:{"light-my-request":`^6.6.0`}},Te=`{{#each routes}}
|
|
4
586
|
import {{id}} from "{{ createImport 'libApi' name 'fetch' }}";
|
|
5
587
|
{{/each}}
|
|
6
588
|
|
|
@@ -18,10 +600,10 @@ export default {
|
|
|
18
600
|
{{/each}}
|
|
19
601
|
{{/each}}
|
|
20
602
|
}
|
|
21
|
-
`,
|
|
603
|
+
`,Ee=`import fetchFactory, { join } from "@kosmojs/core/fetch";
|
|
22
604
|
|
|
23
605
|
import { base, apiBase, apiRouteMap } from "{{ createImport 'libCore' }}";
|
|
24
|
-
import { transport } from "
|
|
606
|
+
import { transport } from "virtual:kosmo/fetch-transport";
|
|
25
607
|
|
|
26
608
|
import {
|
|
27
609
|
type MaybeWrapped,
|
|
@@ -73,9 +655,7 @@ export const {{method}} = (
|
|
|
73
655
|
_params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,
|
|
74
656
|
_payload?: {
|
|
75
657
|
{{#each payloadTypes}}
|
|
76
|
-
{{target}}: {{id}}
|
|
77
|
-
? MaybeWrapped<{{id}}["{{method}}"]>
|
|
78
|
-
: unknown,
|
|
658
|
+
{{target}}: MaybeWrapped<{{id}}>,
|
|
79
659
|
{{/each}}
|
|
80
660
|
},
|
|
81
661
|
opt?: {
|
|
@@ -104,27 +684,229 @@ export const {{method}} = (
|
|
|
104
684
|
payloadResolver(payload as never, "{{target}}", "{{../method}}"),
|
|
105
685
|
);
|
|
106
686
|
}
|
|
107
|
-
{{/each}}
|
|
687
|
+
{{/each}}
|
|
688
|
+
}
|
|
689
|
+
return fetchApi.{{method}}(parametrize(params as never), payload as never);
|
|
690
|
+
};
|
|
691
|
+
{{/each}}
|
|
692
|
+
|
|
693
|
+
export default {
|
|
694
|
+
{{#each routeMethods}}
|
|
695
|
+
{{method}},
|
|
696
|
+
{{/each}}
|
|
697
|
+
path,
|
|
698
|
+
href,
|
|
699
|
+
validationSchemas,
|
|
700
|
+
};
|
|
701
|
+
`,De=`import backend from "virtual:kosmo/backend-app";
|
|
702
|
+
|
|
703
|
+
import {
|
|
704
|
+
redirectCodes,
|
|
705
|
+
ssrOrigin,
|
|
706
|
+
store,
|
|
707
|
+
} from "{{ createImport 'libCore' 'ssr' }}";
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Maximum redirect hops, mirroring the fetch spec limit.
|
|
711
|
+
* */
|
|
712
|
+
export const maxRedirects = 5;
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* HeadersProvider for createTransport.
|
|
716
|
+
* */
|
|
717
|
+
const headersProvider = () => {
|
|
718
|
+
return store.getStore()?.headers;
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const createDispatch = (app) => {
|
|
722
|
+
return typeof app.fetch === "function"
|
|
723
|
+
? app.fetch
|
|
724
|
+
: async (request) => {
|
|
725
|
+
const { inject } = await import("light-my-request");
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Node dispatch: serializes the web Request into light-my-request's
|
|
729
|
+
* injection format and lifts the injected response back into a web Response.
|
|
730
|
+
* */
|
|
731
|
+
const url = new URL(request.url);
|
|
732
|
+
|
|
733
|
+
const payload = ["GET", "HEAD"].includes(request.method)
|
|
734
|
+
? undefined
|
|
735
|
+
: Buffer.from(await request.arrayBuffer());
|
|
736
|
+
|
|
737
|
+
const result = await inject(app.callback(), {
|
|
738
|
+
method: request.method,
|
|
739
|
+
url: url.pathname + url.search,
|
|
740
|
+
headers: Object.fromEntries(request.headers),
|
|
741
|
+
...(payload?.length ? { payload } : {}),
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
const headers = new Headers();
|
|
745
|
+
|
|
746
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
747
|
+
for (const entry of Array.isArray(value) ? value : [value]) {
|
|
748
|
+
if (entry !== undefined) {
|
|
749
|
+
headers.append(key, String(entry));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* 204/304 responses must not carry a body per the Response
|
|
756
|
+
* constructor contract.
|
|
757
|
+
* */
|
|
758
|
+
const body = [204, 304].includes(result.statusCode)
|
|
759
|
+
? null
|
|
760
|
+
: new Uint8Array(result.rawPayload);
|
|
761
|
+
|
|
762
|
+
return new Response(body, {
|
|
763
|
+
status: result.statusCode,
|
|
764
|
+
statusText: result.statusMessage,
|
|
765
|
+
headers,
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
const createTransport = (app) => {
|
|
771
|
+
const dispatch = createDispatch(app);
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Build a fetch-compatible transport that dispatches requests
|
|
775
|
+
* directly into the given app - no sockets, no interception.
|
|
776
|
+
* Redirects are followed in-process, including the 303 and 301/302 method rewrite to GET.
|
|
777
|
+
* */
|
|
778
|
+
return async (input, init) => {
|
|
779
|
+
/**
|
|
780
|
+
* Request-scoped headers act as defaults: anything set explicitly
|
|
781
|
+
* on the call itself wins over forwarded values.
|
|
782
|
+
* */
|
|
783
|
+
const headers = new Headers(init?.headers);
|
|
784
|
+
|
|
785
|
+
// When the body is FormData, the Request constructor sets a multipart
|
|
786
|
+
// Content-Type with a fresh boundary. A forwarded Content-Type default would
|
|
787
|
+
// override that boundary and desync it from the serialized body, so never
|
|
788
|
+
// forward Content-Type for FormData bodies.
|
|
789
|
+
const isFormBody = init?.body instanceof FormData;
|
|
790
|
+
|
|
791
|
+
for (const [key, value] of new Headers(headersProvider() || undefined)) {
|
|
792
|
+
if (isFormBody && key.toLowerCase() === "content-type") {
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (!headers.has(key)) {
|
|
796
|
+
headers.set(key, value);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
let request = new Request(new URL(String(input), ssrOrigin), {
|
|
801
|
+
...init,
|
|
802
|
+
headers,
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Bodies are buffered once so they can be replayed across
|
|
807
|
+
* 307/308 hops; the client only ever sends strings, FormData
|
|
808
|
+
* and buffer-ish payloads, so this is safe and cheap.
|
|
809
|
+
* */
|
|
810
|
+
const body = ["GET", "HEAD"].includes(request.method)
|
|
811
|
+
? undefined
|
|
812
|
+
: await request.arrayBuffer();
|
|
813
|
+
|
|
814
|
+
for (let hop = 0; ; hop++) {
|
|
815
|
+
if (hop === maxRedirects) {
|
|
816
|
+
throw new TypeError("Failed to fetch: too many redirects");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const response = await dispatch(
|
|
820
|
+
body === undefined || request.method === "GET"
|
|
821
|
+
? new Request(request, { body: null })
|
|
822
|
+
: new Request(request, { body }),
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
const location = response.headers.get("location");
|
|
826
|
+
|
|
827
|
+
if (!location || !redirectCodes.includes(response.status)) {
|
|
828
|
+
return response;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const method =
|
|
832
|
+
response.status === 303 ||
|
|
833
|
+
([301, 302].includes(response.status) && request.method === "POST")
|
|
834
|
+
? "GET"
|
|
835
|
+
: request.method;
|
|
836
|
+
|
|
837
|
+
request = new Request(new URL(location, request.url), {
|
|
838
|
+
method,
|
|
839
|
+
headers: request.headers,
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
const ssrTransport = backend ? createTransport(backend) : undefined;
|
|
846
|
+
|
|
847
|
+
export const transport = ssrTransport
|
|
848
|
+
? async (input, init) => {
|
|
849
|
+
try {
|
|
850
|
+
const response = await ssrTransport(input, init);
|
|
851
|
+
if (response?.ok) {
|
|
852
|
+
return response;
|
|
853
|
+
}
|
|
854
|
+
// the rethrow here needed cause ssrTransport does not throw on non-2xx responses
|
|
855
|
+
throw new SSRFetchError([
|
|
856
|
+
input,
|
|
857
|
+
response,
|
|
858
|
+
typeof response?.text === "function"
|
|
859
|
+
? await response.text()
|
|
860
|
+
: response?.statusText,
|
|
861
|
+
]);
|
|
862
|
+
} catch (error) {
|
|
863
|
+
/**
|
|
864
|
+
* Capture the fetch error at the transport level and stash it on the request store.
|
|
865
|
+
* Some frameworks - Solid notably - swallow a rejecting loader and still emit a partial render tree.
|
|
866
|
+
* Storing the error here keeps it observable regardless of how the framework handles the loader rejection.
|
|
867
|
+
* */
|
|
868
|
+
const storage = store.getStore();
|
|
869
|
+
if (storage) {
|
|
870
|
+
storage.error = error;
|
|
871
|
+
}
|
|
872
|
+
throw error;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
: undefined; // let fetch clients pick the transport
|
|
876
|
+
|
|
877
|
+
class SSRFetchError extends Error {
|
|
878
|
+
constructor([input, response, message]) {
|
|
879
|
+
const pathname = pathnameOf(input);
|
|
880
|
+
const status = response.status ?? "unknown";
|
|
881
|
+
super(\`\${pathname}: \${status} [ \${message} ]\`.trim());
|
|
882
|
+
this.name = "SSRFetchError";
|
|
108
883
|
}
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
{{/each}}
|
|
884
|
+
}
|
|
112
885
|
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
886
|
+
const pathnameOf = (input) => {
|
|
887
|
+
try {
|
|
888
|
+
if (typeof input === "string") {
|
|
889
|
+
return new URL(input, "http://x").pathname;
|
|
890
|
+
}
|
|
891
|
+
if (input instanceof URL) {
|
|
892
|
+
return input.pathname;
|
|
893
|
+
}
|
|
894
|
+
if (input instanceof Request) {
|
|
895
|
+
return new URL(input.url).pathname;
|
|
896
|
+
}
|
|
897
|
+
} catch {}
|
|
898
|
+
return String(input);
|
|
120
899
|
};
|
|
121
|
-
`,
|
|
900
|
+
`,Oe=`export type MaybeWrapped<T> = T;
|
|
122
901
|
export const unwrap = <T>(data: T) => data;
|
|
123
|
-
`,
|
|
902
|
+
`,ke=v(e=>{let{createPath:t,createImportHelpers:n}=S(e),{render:r,renderToFile:i}=w({helpers:{...n({origin:`lib`}),...A()}}),a=async(e,n)=>{let r=e.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]).sort(E);await i(t.lib(`fetch.ts`),Te,{routes:r});for(let{kind:e,entry:r}of n)if(e===`apiRoute`){let e=[];for(let t of r.validationDefinitions)if(t.target===`response`)for(let{id:n,status:r,body:i,resolvedType:a}of t.variants)!i||Math.floor(r/100)!==2||e.push({id:n,target:t.target,method:t.method,resolvedType:a});else{let{id:n,resolvedType:r}=t.schema;e.push({id:n,target:t.target,method:t.method,resolvedType:r})}let n=Object.keys(te),a=r.methods.flatMap(t=>n.includes(t)?[{method:t,payloadTypes:e.filter(e=>e.method===t&&![`headers`,`cookies`,`response`].includes(e.target)),responseType:e.find(e=>e.target===`response`&&e.method===t)}]:[]),o=Object.values(e.reduce((e,{id:t,target:n,method:r,resolvedType:i})=>(n===`response`&&(e[r]||(e[r]={method:r,types:[]}),e[r].types.push({id:t,target:n,method:r,resolvedType:i})),e),{}));await i(t.libApi(r.name,`fetch.ts`),Ee,{route:r,validationTypes:e,routeMethods:a,responseTypes:o})}};return{async start(){for(let[e,n]of[[`unwrap.ts`,Oe]])await i(t.lib(e),n,{})},async watch(e,t){await a(e,e.filter(h(t,[`create`,`update`])))},async build(e){await a(e,e)},virtualModules(){return[{specifier:`virtual:kosmo/fetch-transport`,csr:`export const transport = undefined;`,ssr:r(De,{})}]}}}),Ae=_({meta:{name:`Fetch`,slot:`fetch`},dependencies:{"light-my-request":we.devDependencies[`light-my-request`]},factory:ke}),je={type:`module`,private:!0,name:`@kosmojs/h3-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/h3-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,crc:`^4.3.2`},devDependencies:{h3:`2.0.1-rc.29`,vite:`^8.2.2`}},Me=`import { H3, type Middleware } from "h3";
|
|
124
903
|
|
|
125
904
|
import type { Route, RouteDebugOption } from "@kosmojs/core/api";
|
|
126
905
|
|
|
127
|
-
|
|
906
|
+
/**
|
|
907
|
+
* The interface is a nameable symbol of this module, emit stops here and never chases through to the class.
|
|
908
|
+
* */
|
|
909
|
+
export interface App extends H3 {}
|
|
128
910
|
|
|
129
911
|
export type AppOptions = ConstructorParameters<typeof H3>[0] & {
|
|
130
912
|
debug?: RouteDebugOption;
|
|
@@ -168,23 +950,65 @@ export function appFactory(
|
|
|
168
950
|
} else if (debug) {
|
|
169
951
|
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
170
952
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
953
|
+
|
|
954
|
+
// last middleware is the handler
|
|
955
|
+
const handler = route.middleware.at(-1);
|
|
956
|
+
|
|
957
|
+
if (handler) {
|
|
958
|
+
app.on(route.method, route.path, handler as never, {
|
|
959
|
+
middleware: route.middleware.slice(0, -1),
|
|
960
|
+
});
|
|
961
|
+
if (
|
|
962
|
+
route.method === "GET" &&
|
|
963
|
+
!routes.some((e) => e.path === route.path && e.method === "HEAD")
|
|
964
|
+
) {
|
|
965
|
+
/**
|
|
966
|
+
* Register HEAD against the sibling GET handler,
|
|
967
|
+
* matching the HEAD-via-GET dispatch hono and koa provide natively.
|
|
968
|
+
* */
|
|
969
|
+
app.on("HEAD", route.path, handler as never, {
|
|
176
970
|
middleware: route.middleware.slice(0, -1),
|
|
177
971
|
});
|
|
178
972
|
}
|
|
179
973
|
}
|
|
180
974
|
}
|
|
181
975
|
|
|
976
|
+
const routesByPath = routes.reduce<Record<string, Array<Route<Middleware>>>>(
|
|
977
|
+
(map, route) => {
|
|
978
|
+
if (!map[route.path]) {
|
|
979
|
+
map[route.path] = [];
|
|
980
|
+
}
|
|
981
|
+
map[route.path].push(route);
|
|
982
|
+
return map;
|
|
983
|
+
},
|
|
984
|
+
{},
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
for (const [path, routes] of Object.entries(routesByPath)) {
|
|
988
|
+
const methods = routes.map((e) => e.method);
|
|
989
|
+
|
|
990
|
+
app.all(path, (event) => {
|
|
991
|
+
const allowedMethods = new Set([...methods, "OPTIONS"]);
|
|
992
|
+
|
|
993
|
+
if (methods.includes("GET")) {
|
|
994
|
+
allowedMethods.add("HEAD");
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const status = event.req.method === "OPTIONS" ? 204 : 405;
|
|
998
|
+
|
|
999
|
+
return new Response(undefined, {
|
|
1000
|
+
status,
|
|
1001
|
+
headers: { Allow: [...allowedMethods].join(", ") },
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
|
|
182
1006
|
return app;
|
|
183
1007
|
}
|
|
184
|
-
`,
|
|
1008
|
+
`,Ne=`import type { DevSetup } from "@kosmojs/core/api";
|
|
185
1009
|
|
|
186
1010
|
export const devSetup = (setup: DevSetup) => setup;
|
|
187
|
-
`,
|
|
1011
|
+
`,Pe=`import { type H3Event, HTTPError } from "h3";
|
|
188
1012
|
|
|
189
1013
|
import { ValidationError } from "@kosmojs/core/errors";
|
|
190
1014
|
|
|
@@ -208,7 +1032,17 @@ export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
|
208
1032
|
);
|
|
209
1033
|
};
|
|
210
1034
|
};
|
|
211
|
-
`,
|
|
1035
|
+
`,Fe=`import { createListener } from "./server";
|
|
1036
|
+
|
|
1037
|
+
import app from "{{ createImport 'api' 'app' }}";
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Entry for the \`dist/<folder>/api/listener.js\` bundle.
|
|
1041
|
+
* Exposes this folder's API as a plain node:http listener, mounted by \`dist/run.js\`
|
|
1042
|
+
* next to the other folders. Nothing here listens on a port - \`server.ts\` does that.
|
|
1043
|
+
* */
|
|
1044
|
+
export default createListener(app);
|
|
1045
|
+
`,Ie=`import { type H3Event, readBody } from "h3";
|
|
212
1046
|
|
|
213
1047
|
import {
|
|
214
1048
|
parseCookies,
|
|
@@ -248,7 +1082,8 @@ export const bodyparsers: {
|
|
|
248
1082
|
return event.req.text();
|
|
249
1083
|
},
|
|
250
1084
|
};
|
|
251
|
-
`,
|
|
1085
|
+
`,Le=`import { command } from "virtual:kosmo/env";
|
|
1086
|
+
import type { Middleware } from "h3";
|
|
252
1087
|
|
|
253
1088
|
import type {
|
|
254
1089
|
RequestBodyTarget,
|
|
@@ -333,7 +1168,9 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
333
1168
|
target === "query"
|
|
334
1169
|
? normalizeSearchParams(
|
|
335
1170
|
parser(event),
|
|
336
|
-
event.req.method
|
|
1171
|
+
event.req.method === "HEAD"
|
|
1172
|
+
? "GET"
|
|
1173
|
+
: event.req.method,
|
|
337
1174
|
)
|
|
338
1175
|
: parser(event),
|
|
339
1176
|
);
|
|
@@ -411,7 +1248,10 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
411
1248
|
* */
|
|
412
1249
|
use(
|
|
413
1250
|
async function useValidateResponse(event, next) {
|
|
414
|
-
const variants =
|
|
1251
|
+
const variants =
|
|
1252
|
+
validationSchemas.response?.[
|
|
1253
|
+
event.req.method === "HEAD" ? "GET" : event.req.method
|
|
1254
|
+
] || [];
|
|
415
1255
|
|
|
416
1256
|
if (!Array.isArray(variants) || !variants.length) {
|
|
417
1257
|
return next();
|
|
@@ -420,13 +1260,13 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
420
1260
|
// options are same for all variants
|
|
421
1261
|
const { runtimeValidation, customErrors } = variants[0];
|
|
422
1262
|
|
|
423
|
-
if (
|
|
424
|
-
// skip if undefined or explicitly set to false
|
|
1263
|
+
if (command === "build") {
|
|
1264
|
+
// production build - skip if undefined or explicitly set to false
|
|
425
1265
|
if (runtimeValidation === undefined || runtimeValidation === false) {
|
|
426
1266
|
return next();
|
|
427
1267
|
}
|
|
428
1268
|
} else {
|
|
429
|
-
// skip only if explicitly set to false
|
|
1269
|
+
// dev mode - skip only if explicitly set to false
|
|
430
1270
|
if (runtimeValidation === false) {
|
|
431
1271
|
return next();
|
|
432
1272
|
}
|
|
@@ -460,6 +1300,11 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
460
1300
|
: "application/json"),
|
|
461
1301
|
};
|
|
462
1302
|
|
|
1303
|
+
// validate only 2xx responses
|
|
1304
|
+
if (Math.floor(response.status / 100) !== 2) {
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
463
1308
|
// Validate body only for JSON variants
|
|
464
1309
|
if (variants.some((e) => e.contentType?.includes("json"))) {
|
|
465
1310
|
response.body = rawResponse
|
|
@@ -598,7 +1443,9 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
598
1443
|
use(
|
|
599
1444
|
async (event, next) => {
|
|
600
1445
|
const schema = {
|
|
601
|
-
...validationSchemas[target]?.[
|
|
1446
|
+
...validationSchemas[target]?.[
|
|
1447
|
+
event.req.method === "HEAD" ? "GET" : event.req.method
|
|
1448
|
+
],
|
|
602
1449
|
};
|
|
603
1450
|
if (schema.validate && schema.runtimeValidation !== false) {
|
|
604
1451
|
schema.validate(await loadData(event as never));
|
|
@@ -628,7 +1475,7 @@ export const routes = createRoutes<ParameterizedMiddleware, Middleware>(
|
|
|
628
1475
|
createRouteMiddleware,
|
|
629
1476
|
},
|
|
630
1477
|
);
|
|
631
|
-
`,
|
|
1478
|
+
`,Re=`import { join } from "node:path";
|
|
632
1479
|
|
|
633
1480
|
import type { RouteSource } from "@kosmojs/core/api";
|
|
634
1481
|
|
|
@@ -680,12 +1527,25 @@ export const routeSources: Array<RouteSource<never>> = [
|
|
|
680
1527
|
},
|
|
681
1528
|
{{/each}}
|
|
682
1529
|
];
|
|
683
|
-
`,
|
|
1530
|
+
`,ze=`import type { IncomingMessage, ServerResponse } from "node:http";
|
|
1531
|
+
import { parseArgs, styleText } from "node:util";
|
|
684
1532
|
|
|
685
1533
|
import { serve as h3serve } from "h3";
|
|
1534
|
+
import { toNodeHandler } from "h3/node";
|
|
686
1535
|
|
|
687
1536
|
import type { App } from "./app";
|
|
688
1537
|
|
|
1538
|
+
export type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Wrap the app into a node:http request listener.
|
|
1542
|
+
* Used by dist/run.js to mount this folder's API next to other folders;
|
|
1543
|
+
* the standalone server (\`serve\`) binds the app through h3's own adapter instead.
|
|
1544
|
+
* */
|
|
1545
|
+
export const createListener = <T extends App>(app: T): NodeListener => {
|
|
1546
|
+
return toNodeHandler(app);
|
|
1547
|
+
};
|
|
1548
|
+
|
|
689
1549
|
type Handles = {
|
|
690
1550
|
port?: number | undefined;
|
|
691
1551
|
onListen?: () => Promise<void>;
|
|
@@ -728,7 +1588,7 @@ export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
|
728
1588
|
|
|
729
1589
|
return server as never;
|
|
730
1590
|
};
|
|
731
|
-
`,
|
|
1591
|
+
`,Be=`import type { H3Event, H3EventContext } from "h3";
|
|
732
1592
|
|
|
733
1593
|
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
734
1594
|
import {
|
|
@@ -875,22 +1735,22 @@ export const defineRoute: <
|
|
|
875
1735
|
use: use as never,
|
|
876
1736
|
});
|
|
877
1737
|
};
|
|
878
|
-
`,
|
|
1738
|
+
`,Ve=`export * from "./@api/app";
|
|
879
1739
|
export { appFactory as default } from "./@api/app";
|
|
880
1740
|
export * from "./@api/dev";
|
|
881
1741
|
export * from "./@api/errors";
|
|
882
1742
|
export * from "./@api/router";
|
|
883
1743
|
export * from "./@api/routes";
|
|
884
1744
|
export * from "./@api/server";
|
|
885
|
-
`,
|
|
1745
|
+
`,He=`import { onError } from "h3";
|
|
886
1746
|
|
|
887
|
-
import appFactory, { routes
|
|
1747
|
+
import appFactory, { routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
888
1748
|
import defaultErrorHandler from "./errors";
|
|
889
1749
|
|
|
890
1750
|
export default appFactory(routes, ({ app }) => {
|
|
891
1751
|
app.use(onError(defaultErrorHandler));
|
|
892
|
-
})
|
|
893
|
-
`,
|
|
1752
|
+
});
|
|
1753
|
+
`,Ue=`import { toNodeHandler } from "h3/node";
|
|
894
1754
|
|
|
895
1755
|
import app from "./app";
|
|
896
1756
|
|
|
@@ -910,10 +1770,10 @@ process.on("unhandledRejection", (reason) => {
|
|
|
910
1770
|
console.error("Reason:", reason);
|
|
911
1771
|
process.exit(1);
|
|
912
1772
|
});
|
|
913
|
-
`,
|
|
1773
|
+
`,We=`export declare module "{{ createImport 'libApi' }}" {
|
|
914
1774
|
interface DefaultContext {}
|
|
915
1775
|
}
|
|
916
|
-
`,
|
|
1776
|
+
`,Ge=`import { ValidationError } from "@kosmojs/core/errors";
|
|
917
1777
|
import { HTTPError } from "h3";
|
|
918
1778
|
|
|
919
1779
|
import { errorHandlerFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
@@ -939,14 +1799,14 @@ export default errorHandlerFactory(async (error, event) => {
|
|
|
939
1799
|
headers: { "Content-Type": "text/plain" },
|
|
940
1800
|
});
|
|
941
1801
|
});
|
|
942
|
-
`,
|
|
1802
|
+
`,Ke=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
943
1803
|
|
|
944
1804
|
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
945
1805
|
GET(async (event) => {
|
|
946
1806
|
return "Automatically generated route";
|
|
947
1807
|
}),
|
|
948
1808
|
]);
|
|
949
|
-
`,
|
|
1809
|
+
`,qe=`import { use } from "{{ createImport 'libApi' }}";
|
|
950
1810
|
|
|
951
1811
|
export type UseT = {};
|
|
952
1812
|
|
|
@@ -955,11 +1815,11 @@ export default [
|
|
|
955
1815
|
return next();
|
|
956
1816
|
}),
|
|
957
1817
|
];
|
|
958
|
-
`,
|
|
1818
|
+
`,Je=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
959
1819
|
import app from "./app";
|
|
960
1820
|
|
|
961
1821
|
await serve(app);
|
|
962
|
-
`,
|
|
1822
|
+
`,Ye=`import { use } from "{{ createImport 'libApi' }}";
|
|
963
1823
|
|
|
964
1824
|
/**
|
|
965
1825
|
* Define global middleware applied to all routes.
|
|
@@ -970,7 +1830,7 @@ export default [
|
|
|
970
1830
|
return next();
|
|
971
1831
|
}),
|
|
972
1832
|
];
|
|
973
|
-
`,
|
|
1833
|
+
`,Xe=v((e,n)=>{let{createPath:r,createImportHelpers:i}=S(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=w({helpers:{...i({origin:`lib`}),...A(),paramsDefaults({params:e}){return`[${e.schema.map(()=>`unknown?`).join(`, `)}]`},paramsMappings({params:e}){return`[${e.schema.map(({name:e,kind:t})=>`["${e}", unknown, ${t===`required`?`true`:`false`}]`).join(`, `)}]`},cascadingState({cascadingMiddleware:e}){return a(e.map(({id:e})=>`UseT${e}`))}}}),{renderToFile:s}=w({helpers:i({origin:`src`})}),c=e=>e?.trim().length===0,u=l(n?.templates,Ke),d=async e=>{for(let{kind:t,entry:n}of e)t===`apiRoute`?await s(r.api(n.file),u(n.name,n),{route:n},{overwrite:c}):t===`apiUse`&&await s(r.api(n.file),qe,{},{overwrite:c})},p=async e=>{let i=e.flatMap(({kind:e,entry:t})=>e===`apiUse`?[t]:[]),a=e.flatMap(({kind:e,entry:r})=>{if(e!==`apiRoute`)return[];let a=r.name.split(`/`).reduce((e,n)=>{let r=e[e.length-1];return e.push(r?t(r,n):n),e},[]),o={...r,path:r.h3Pattern,basename:r.name,cascadingMiddleware:i.flatMap(e=>a.some(t=>e.name===t)?[e]:[])};return[o,...Object.entries({...n?.alias}).flatMap(([e,t])=>{let n=C(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${j(e)}`,alias:f(n),pathTokens:n}]:[]})]}).sort(E);for(let[e,t]of[[`@api/routes.ts`,Re]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{async start(){for(let[e,t]of[[`api.ts`,Be],[`api:factory.ts`,Ve],[`@api/app.ts`,Me],[`@api/parsers.ts`,Ie],[`@api/dev.ts`,Ne],[`@api/errors.ts`,Pe],[`@api/listener.ts`,Fe],[`@api/router.ts`,Le],[`@api/server.ts`,ze]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,He],[`dev.ts`,Ue],[`errors.ts`,Ge],[`server.ts`,Je],[`use.ts`,Ye],[`env.d.ts`,We]])await s(r.api(e),t,{},{overwrite:c})},async watch(e,t){await d(e.filter(h(t,[`create`]))),await p(e)},async build(e){await d(e),await p(e)}}}),Ze=_({meta:{name:`H3`,slot:`backend`},dependencies:{h3:je.devDependencies.h3},factory:Xe}),V={type:`module`,private:!0,name:`@kosmojs/hono-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/hono-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,crc:`^4.3.2`},devDependencies:{"@hono/node-server":`^2.1.1`,hono:`^4.13.3`,vite:`^8.2.2`}},Qe=`import { Hono, type MiddlewareHandler } from "hono";
|
|
974
1834
|
import type { Router } from "hono/router";
|
|
975
1835
|
import { RegExpRouter } from "hono/router/reg-exp-router";
|
|
976
1836
|
import { SmartRouter } from "hono/router/smart-router";
|
|
@@ -1037,15 +1897,45 @@ export function appFactory(
|
|
|
1037
1897
|
} else if (debug) {
|
|
1038
1898
|
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
1039
1899
|
}
|
|
1040
|
-
|
|
1900
|
+
|
|
1901
|
+
app.on(route.method, [route.path], ...route.middleware);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
const routesByPath = routes.reduce<
|
|
1905
|
+
Record<string, Array<Route<MiddlewareHandler>>>
|
|
1906
|
+
>((map, route) => {
|
|
1907
|
+
if (!map[route.path]) {
|
|
1908
|
+
map[route.path] = [];
|
|
1909
|
+
}
|
|
1910
|
+
map[route.path].push(route);
|
|
1911
|
+
return map;
|
|
1912
|
+
}, {});
|
|
1913
|
+
|
|
1914
|
+
for (const [path, routes] of Object.entries(routesByPath)) {
|
|
1915
|
+
const methods = routes.map((e) => e.method);
|
|
1916
|
+
|
|
1917
|
+
app.all(path, (ctx) => {
|
|
1918
|
+
const allowedMethods = new Set([...methods, "OPTIONS"]);
|
|
1919
|
+
|
|
1920
|
+
if (methods.includes("GET")) {
|
|
1921
|
+
allowedMethods.add("HEAD");
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
const status = ctx.req.method === "OPTIONS" ? 204 : 405;
|
|
1925
|
+
|
|
1926
|
+
return new Response(undefined, {
|
|
1927
|
+
status,
|
|
1928
|
+
headers: { Allow: [...allowedMethods].join(", ") },
|
|
1929
|
+
});
|
|
1930
|
+
});
|
|
1041
1931
|
}
|
|
1042
1932
|
|
|
1043
1933
|
return app as never;
|
|
1044
1934
|
}
|
|
1045
|
-
|
|
1935
|
+
`,$e=`import type { DevSetup } from "@kosmojs/core/api";
|
|
1046
1936
|
|
|
1047
1937
|
export const devSetup = (setup: DevSetup) => setup;
|
|
1048
|
-
`,
|
|
1938
|
+
`,et=`import type { Context } from "hono";
|
|
1049
1939
|
|
|
1050
1940
|
import type { AppEnv } from "./app";
|
|
1051
1941
|
|
|
@@ -1059,7 +1949,17 @@ export type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;
|
|
|
1059
1949
|
export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
1060
1950
|
return handler;
|
|
1061
1951
|
};
|
|
1062
|
-
`,
|
|
1952
|
+
`,tt=`import { createListener } from "./server";
|
|
1953
|
+
|
|
1954
|
+
import app from "{{ createImport 'api' 'app' }}";
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Entry for the \`dist/<folder>/api/listener.js\` bundle.
|
|
1958
|
+
* Exposes this folder's API as a plain node:http listener, mounted by \`dist/run.js\`
|
|
1959
|
+
* next to the other folders. Nothing here listens on a port - \`server.ts\` does that.
|
|
1960
|
+
* */
|
|
1961
|
+
export default createListener(app);
|
|
1962
|
+
`,nt=`import type { Context } from "hono";
|
|
1063
1963
|
|
|
1064
1964
|
import {
|
|
1065
1965
|
parseCookies,
|
|
@@ -1108,7 +2008,8 @@ export const bodyparsers: {
|
|
|
1108
2008
|
return ctx.req[as]();
|
|
1109
2009
|
},
|
|
1110
2010
|
};
|
|
1111
|
-
`,
|
|
2011
|
+
`,rt=`import { command } from "virtual:kosmo/env";
|
|
2012
|
+
import type { MiddlewareHandler } from "hono";
|
|
1112
2013
|
|
|
1113
2014
|
import type {
|
|
1114
2015
|
RequestBodyTarget,
|
|
@@ -1194,7 +2095,7 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1194
2095
|
target === "query"
|
|
1195
2096
|
? normalizeSearchParams(
|
|
1196
2097
|
parser(ctx),
|
|
1197
|
-
ctx.req.method
|
|
2098
|
+
ctx.req.method === "HEAD" ? "GET" : ctx.req.method,
|
|
1198
2099
|
)
|
|
1199
2100
|
: parser(ctx),
|
|
1200
2101
|
);
|
|
@@ -1274,7 +2175,10 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1274
2175
|
* */
|
|
1275
2176
|
use(
|
|
1276
2177
|
async function useValidateResponse(ctx, next) {
|
|
1277
|
-
const variants =
|
|
2178
|
+
const variants =
|
|
2179
|
+
validationSchemas.response?.[
|
|
2180
|
+
ctx.req.method === "HEAD" ? "GET" : ctx.req.method
|
|
2181
|
+
] || [];
|
|
1278
2182
|
|
|
1279
2183
|
if (!Array.isArray(variants) || !variants.length) {
|
|
1280
2184
|
return next();
|
|
@@ -1283,13 +2187,13 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1283
2187
|
// options are same for all variants
|
|
1284
2188
|
const { runtimeValidation, customErrors } = variants[0];
|
|
1285
2189
|
|
|
1286
|
-
if (
|
|
1287
|
-
// skip if undefined or explicitly set to false
|
|
2190
|
+
if (command === "build") {
|
|
2191
|
+
// production build - skip if undefined or explicitly set to false
|
|
1288
2192
|
if (runtimeValidation === undefined || runtimeValidation === false) {
|
|
1289
2193
|
return next();
|
|
1290
2194
|
}
|
|
1291
2195
|
} else {
|
|
1292
|
-
// skip only if explicitly set to false
|
|
2196
|
+
// dev mode - skip only if explicitly set to false
|
|
1293
2197
|
if (runtimeValidation === false) {
|
|
1294
2198
|
return next();
|
|
1295
2199
|
}
|
|
@@ -1307,10 +2211,17 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1307
2211
|
contentType: ctx.res.headers.get("Content-Type"),
|
|
1308
2212
|
};
|
|
1309
2213
|
|
|
2214
|
+
// validate only 2xx responses
|
|
2215
|
+
if (Math.floor(response.status / 100) !== 2) {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
1310
2219
|
// Validate body only for JSON variants
|
|
1311
2220
|
if (variants.some((e) => e.contentType?.includes("json"))) {
|
|
1312
|
-
|
|
1313
|
-
|
|
2221
|
+
response.body = await ctx.res
|
|
2222
|
+
.clone()
|
|
2223
|
+
.json()
|
|
2224
|
+
.catch(() => undefined);
|
|
1314
2225
|
}
|
|
1315
2226
|
|
|
1316
2227
|
/**
|
|
@@ -1445,7 +2356,9 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1445
2356
|
use(
|
|
1446
2357
|
async (ctx, next) => {
|
|
1447
2358
|
const schema = {
|
|
1448
|
-
...validationSchemas[target]?.[
|
|
2359
|
+
...validationSchemas[target]?.[
|
|
2360
|
+
ctx.req.method === "HEAD" ? "GET" : ctx.req.method
|
|
2361
|
+
],
|
|
1449
2362
|
};
|
|
1450
2363
|
if (schema.validate && schema.runtimeValidation !== false) {
|
|
1451
2364
|
schema.validate(await loadData(ctx as never));
|
|
@@ -1475,7 +2388,7 @@ export const routes = createRoutes<ParameterizedMiddleware, MiddlewareHandler>(
|
|
|
1475
2388
|
createRouteMiddleware,
|
|
1476
2389
|
},
|
|
1477
2390
|
);
|
|
1478
|
-
`,
|
|
2391
|
+
`,it=`import { join } from "node:path";
|
|
1479
2392
|
|
|
1480
2393
|
import type { RouteSource } from "@kosmojs/core/api";
|
|
1481
2394
|
|
|
@@ -1527,13 +2440,25 @@ export const routeSources: Array<RouteSource<never>> = [
|
|
|
1527
2440
|
},
|
|
1528
2441
|
{{/each}}
|
|
1529
2442
|
];
|
|
1530
|
-
`,
|
|
2443
|
+
`,at=`import { chmod, unlink } from "node:fs/promises";
|
|
2444
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
1531
2445
|
import { parseArgs, styleText } from "node:util";
|
|
1532
2446
|
|
|
1533
|
-
import { createAdaptorServer } from "@hono/node-server";
|
|
2447
|
+
import { createAdaptorServer, getRequestListener } from "@hono/node-server";
|
|
1534
2448
|
|
|
1535
2449
|
import type { App } from "./app";
|
|
1536
2450
|
|
|
2451
|
+
export type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;
|
|
2452
|
+
|
|
2453
|
+
/**
|
|
2454
|
+
* Wrap the app into a node:http request listener.
|
|
2455
|
+
* Used by dist/run.js to mount this folder's API next to other folders;
|
|
2456
|
+
* the standalone server (\`serve\`) binds the app through the runtime's native adapter instead.
|
|
2457
|
+
* */
|
|
2458
|
+
export const createListener = <T extends App>(app: T): NodeListener => {
|
|
2459
|
+
return getRequestListener(app.fetch);
|
|
2460
|
+
};
|
|
2461
|
+
|
|
1537
2462
|
type Handles = {
|
|
1538
2463
|
port?: number | undefined;
|
|
1539
2464
|
sock?: string | undefined;
|
|
@@ -1612,7 +2537,7 @@ export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
|
1612
2537
|
|
|
1613
2538
|
return server as never;
|
|
1614
2539
|
};
|
|
1615
|
-
`,
|
|
2540
|
+
`,ot=`import type { Context, Next } from "hono";
|
|
1616
2541
|
|
|
1617
2542
|
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
1618
2543
|
import {
|
|
@@ -1795,20 +2720,20 @@ export const defineRoute: <
|
|
|
1795
2720
|
use: use as never,
|
|
1796
2721
|
});
|
|
1797
2722
|
};
|
|
1798
|
-
`,
|
|
2723
|
+
`,st=`export * from "./@api/app";
|
|
1799
2724
|
export { appFactory as default } from "./@api/app";
|
|
1800
2725
|
export * from "./@api/dev";
|
|
1801
2726
|
export * from "./@api/errors";
|
|
1802
2727
|
export * from "./@api/router";
|
|
1803
2728
|
export * from "./@api/routes";
|
|
1804
2729
|
export * from "./@api/server";
|
|
1805
|
-
`,
|
|
2730
|
+
`,ct=`import appFactory, { routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1806
2731
|
import defaultErrorHandler from "./errors";
|
|
1807
2732
|
|
|
1808
2733
|
export default appFactory(routes, ({ app }) => {
|
|
1809
2734
|
app.onError(defaultErrorHandler);
|
|
1810
2735
|
})
|
|
1811
|
-
`,
|
|
2736
|
+
`,lt=`import { getRequestListener } from "@hono/node-server";
|
|
1812
2737
|
|
|
1813
2738
|
import app from "./app";
|
|
1814
2739
|
|
|
@@ -1829,11 +2754,11 @@ process.on("unhandledRejection", (reason) => {
|
|
|
1829
2754
|
process.exit(1);
|
|
1830
2755
|
});
|
|
1831
2756
|
|
|
1832
|
-
`,
|
|
2757
|
+
`,ut=`export declare module "{{ createImport 'libApi' }}" {
|
|
1833
2758
|
interface DefaultVariables {}
|
|
1834
2759
|
interface DefaultBindings {}
|
|
1835
2760
|
}
|
|
1836
|
-
`,
|
|
2761
|
+
`,dt=`import { accepts } from "hono/accepts";
|
|
1837
2762
|
import { HTTPException } from "hono/http-exception";
|
|
1838
2763
|
|
|
1839
2764
|
import { ValidationError, HTTPError } from "@kosmojs/core/errors";
|
|
@@ -1865,7 +2790,7 @@ export default errorHandlerFactory(async (error, ctx) => {
|
|
|
1865
2790
|
? ctx.json({ error: message }, status)
|
|
1866
2791
|
: ctx.text(message, status);
|
|
1867
2792
|
});
|
|
1868
|
-
`,
|
|
2793
|
+
`,ft=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
1869
2794
|
|
|
1870
2795
|
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
1871
2796
|
GET(async (ctx) => {
|
|
@@ -1874,7 +2799,7 @@ export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
|
1874
2799
|
return ctx.text("Automatically generated route");
|
|
1875
2800
|
}),
|
|
1876
2801
|
]);
|
|
1877
|
-
`,
|
|
2802
|
+
`,pt=`import { use } from "{{ createImport 'libApi' }}";
|
|
1878
2803
|
|
|
1879
2804
|
export type UseT = {};
|
|
1880
2805
|
|
|
@@ -1885,11 +2810,11 @@ export default [
|
|
|
1885
2810
|
return next();
|
|
1886
2811
|
}),
|
|
1887
2812
|
];
|
|
1888
|
-
`,
|
|
2813
|
+
`,mt=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1889
2814
|
import app from "./app";
|
|
1890
2815
|
|
|
1891
2816
|
await serve(app);
|
|
1892
|
-
`,
|
|
2817
|
+
`,ht=`import { use } from "{{ createImport 'libApi' }}";
|
|
1893
2818
|
|
|
1894
2819
|
/**
|
|
1895
2820
|
* Define global middleware applied to all routes.
|
|
@@ -1900,7 +2825,9 @@ export default [
|
|
|
1900
2825
|
return next();
|
|
1901
2826
|
}),
|
|
1902
2827
|
];
|
|
1903
|
-
`,
|
|
2828
|
+
`,gt=v((e,n)=>{let{createPath:r,createImportHelpers:i}=S(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=w({helpers:{...i({origin:`lib`}),...A(),paramsDefaults({params:e}){return`[${e.schema.map(()=>`unknown?`).join(`, `)}]`},paramsMappings({params:e}){return`[${e.schema.map(({name:e,kind:t})=>`["${e}", unknown, ${t===`required`?`true`:`false`}]`).join(`, `)}]`},cascadingState({cascadingMiddleware:e}){return a(e.map(({id:e})=>`UseT${e}`))}}}),{renderToFile:s}=w({helpers:i({origin:`src`})}),c=e=>e?.trim().length===0,u=l(n?.templates,ft),d=async e=>{for(let{kind:t,entry:n}of e)t===`apiRoute`?await s(r.api(n.file),u(n.name,n),{route:n},{overwrite:c}):t===`apiUse`&&await s(r.api(n.file),pt,{},{overwrite:c})},f=async e=>{let i=e.flatMap(({kind:e,entry:t})=>e===`apiUse`?[t]:[]),a=e.flatMap(({kind:e,entry:r})=>{if(e!==`apiRoute`)return[];let a=r.name.split(`/`).reduce((e,n)=>{let r=e[e.length-1];return e.push(r?t(r,n):n),e},[]),o={...r,path:r.honoPattern,basename:r.name,cascadingMiddleware:i.flatMap(e=>a.some(t=>e.name===t)?[e]:[])};return[o,...Object.entries({...n?.alias}).flatMap(([e,t])=>{let n=C(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${j(e)}`,alias:p(n),pathTokens:n}]:[]})]}).sort(E);for(let[e,t]of[[`@api/routes.ts`,it]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{async start(){for(let[e,t]of[[`api.ts`,ot],[`api:factory.ts`,st],[`@api/app.ts`,Qe],[`@api/parsers.ts`,nt],[`@api/dev.ts`,$e],[`@api/errors.ts`,et],[`@api/listener.ts`,tt],[`@api/router.ts`,rt],[`@api/server.ts`,at]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,ct],[`dev.ts`,lt],[`errors.ts`,dt],[`server.ts`,mt],[`use.ts`,ht],[`env.d.ts`,ut]])await s(r.api(e),t,{},{overwrite:c})},async watch(e,t){await d(e.filter(h(t,[`create`]))),await f(e)},async build(e){await d(e),await f(e)}}}),_t=_({meta:{name:`Hono`,slot:`backend`},dependencies:{hono:V.devDependencies.hono,"@hono/node-server":V.devDependencies[`@hono/node-server`]},factory:gt}),H={type:`module`,private:!0,name:`@kosmojs/koa-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`},"./lib":{types:`./pkg/lib.d.ts`,default:`./pkg/lib.js`}},scripts:{build:`wsbuild src/index.ts src/lib.ts`,test:`vitest --root ../.. --project generators/koa-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,crc:`^4.3.2`},devDependencies:{"@koa/router":`^15.7.0`,"@types/formidable":`^3.5.1`,"@types/koa":`^3.0.3`,"@types/koa-compose":`^3.2.9`,formidable:`^3.5.4`,koa:`^3.2.1`,"koa-compose":`^4.1.0`,"light-my-request":`^6.6.0`,"raw-body":`^4.0.0`,vite:`^8.2.2`}},vt=`import { styleText } from "node:util";
|
|
2829
|
+
|
|
2830
|
+
import Router, { type RouterMiddleware } from "@koa/router";
|
|
1904
2831
|
import Koa from "koa";
|
|
1905
2832
|
|
|
1906
2833
|
import type { Route, RouteDebugOption } from "@kosmojs/core/api";
|
|
@@ -1911,7 +2838,7 @@ export type App = Koa<DefaultState, DefaultContext>;
|
|
|
1911
2838
|
|
|
1912
2839
|
export type AppOptions = ConstructorParameters<
|
|
1913
2840
|
typeof Koa<DefaultState, DefaultContext>
|
|
1914
|
-
>[0] & {
|
|
2841
|
+
>[0] & { debug?: RouteDebugOption };
|
|
1915
2842
|
|
|
1916
2843
|
export function appFactory(
|
|
1917
2844
|
routes: Array<Route<RouterMiddleware>>,
|
|
@@ -1935,51 +2862,98 @@ export function appFactory(
|
|
|
1935
2862
|
): App {
|
|
1936
2863
|
const [options, fn] = typeof rest[0] === "function" ? [{}, rest[0]] : rest;
|
|
1937
2864
|
|
|
1938
|
-
const {
|
|
1939
|
-
router = new Router(),
|
|
1940
|
-
debug = undefined,
|
|
1941
|
-
...appOptions
|
|
1942
|
-
} = {
|
|
2865
|
+
const { debug = undefined, ...appOptions } = {
|
|
1943
2866
|
...(options ? { ...options } : {}),
|
|
1944
2867
|
};
|
|
1945
2868
|
|
|
2869
|
+
const app = new Koa(appOptions);
|
|
2870
|
+
const router = new Router();
|
|
2871
|
+
|
|
1946
2872
|
for (const route of routes) {
|
|
1947
2873
|
if (typeof debug === "function") {
|
|
1948
2874
|
(debug as Function)(route.debug, route);
|
|
1949
2875
|
} else if (debug) {
|
|
1950
2876
|
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
1951
2877
|
}
|
|
1952
|
-
router.register(route.path, route.
|
|
2878
|
+
router.register(route.path, [route.method], route.middleware, route);
|
|
1953
2879
|
}
|
|
1954
2880
|
|
|
1955
|
-
const
|
|
2881
|
+
const routesByPath = routes.reduce<
|
|
2882
|
+
Record<string, Array<Route<RouterMiddleware>>>
|
|
2883
|
+
>((map, route) => {
|
|
2884
|
+
if (!map[route.path]) {
|
|
2885
|
+
map[route.path] = [];
|
|
2886
|
+
}
|
|
2887
|
+
map[route.path].push(route);
|
|
2888
|
+
return map;
|
|
2889
|
+
}, {});
|
|
2890
|
+
|
|
2891
|
+
for (const [path, routes] of Object.entries(routesByPath)) {
|
|
2892
|
+
const methods = routes.map((e) => e.method);
|
|
2893
|
+
|
|
2894
|
+
if (
|
|
2895
|
+
methods.includes("GET") &&
|
|
2896
|
+
methods.indexOf("HEAD") > methods.indexOf("GET")
|
|
2897
|
+
) {
|
|
2898
|
+
console.warn("----");
|
|
2899
|
+
console.warn(
|
|
2900
|
+
\`\${styleText("yellow", "WARN")}: HEAD handler for \${styleText("blue", routes[0].name)} route is unreachable - \${styleText("magenta", "define HEAD before GET")}\`,
|
|
2901
|
+
);
|
|
2902
|
+
console.warn("----");
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
router.all(path, (ctx) => {
|
|
2906
|
+
const allowedMethods = new Set([...methods, "OPTIONS"]);
|
|
2907
|
+
|
|
2908
|
+
if (methods.includes("GET")) {
|
|
2909
|
+
allowedMethods.add("HEAD");
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
ctx.status = ctx.method === "OPTIONS" ? 204 : 405;
|
|
2913
|
+
|
|
2914
|
+
ctx.set("Allow", [...allowedMethods].join(", "));
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
1956
2917
|
|
|
1957
2918
|
if (typeof fn === "function") {
|
|
1958
2919
|
fn({ app, router });
|
|
1959
2920
|
}
|
|
1960
2921
|
|
|
2922
|
+
// NOTE: Routes should be added last, after any middleware
|
|
2923
|
+
app.use(router.routes());
|
|
2924
|
+
|
|
1961
2925
|
return app;
|
|
1962
2926
|
}
|
|
1963
|
-
`,
|
|
2927
|
+
`,yt=`import type { DevSetup } from "@kosmojs/core/api";
|
|
1964
2928
|
|
|
1965
2929
|
export const devSetup = (setup: DevSetup) => setup;
|
|
1966
|
-
`,
|
|
2930
|
+
`,bt=`import type {
|
|
1967
2931
|
DefaultContext,
|
|
1968
2932
|
DefaultState,
|
|
1969
2933
|
ParameterizedContext,
|
|
1970
2934
|
} from "../api";
|
|
1971
2935
|
|
|
1972
2936
|
type ErrorHandler = (
|
|
1973
|
-
error: any,
|
|
1974
2937
|
ctx: ParameterizedContext<unknown, DefaultState, DefaultContext>,
|
|
1975
|
-
|
|
2938
|
+
next: Function,
|
|
2939
|
+
) => Promise<void>;
|
|
1976
2940
|
|
|
1977
2941
|
export type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;
|
|
1978
2942
|
|
|
1979
2943
|
export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
1980
2944
|
return handler;
|
|
1981
2945
|
};
|
|
1982
|
-
`,
|
|
2946
|
+
`,xt=`import { createListener } from "./server";
|
|
2947
|
+
|
|
2948
|
+
import app from "{{ createImport 'api' 'app' }}";
|
|
2949
|
+
|
|
2950
|
+
/**
|
|
2951
|
+
* Entry for the \`dist/<folder>/api/listener.js\` bundle.
|
|
2952
|
+
* Exposes this folder's API as a plain node:http listener, mounted by \`dist/run.js\`
|
|
2953
|
+
* next to the other folders. Nothing here listens on a port - \`server.ts\` does that.
|
|
2954
|
+
* */
|
|
2955
|
+
export default createListener(app);
|
|
2956
|
+
`,St=`import zlib from "node:zlib";
|
|
1983
2957
|
|
|
1984
2958
|
import type { RouterContext } from "@koa/router";
|
|
1985
2959
|
import Formidable, { type Options as FormidableOptions } from "formidable";
|
|
@@ -2192,7 +3166,8 @@ export const bodyparsers: {
|
|
|
2192
3166
|
return rawParser(stream, rawParserOptions);
|
|
2193
3167
|
},
|
|
2194
3168
|
};
|
|
2195
|
-
|
|
3169
|
+
`,Ct=`import { command } from "virtual:kosmo/env";
|
|
3170
|
+
import type { RouterMiddleware } from "@koa/router";
|
|
2196
3171
|
|
|
2197
3172
|
import type {
|
|
2198
3173
|
RequestBodyTarget,
|
|
@@ -2276,7 +3251,10 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
2276
3251
|
ctx[StateKey].set(
|
|
2277
3252
|
target,
|
|
2278
3253
|
target === "query"
|
|
2279
|
-
? normalizeSearchParams(
|
|
3254
|
+
? normalizeSearchParams(
|
|
3255
|
+
parser(ctx),
|
|
3256
|
+
ctx.method === "HEAD" ? "GET" : ctx.method,
|
|
3257
|
+
)
|
|
2280
3258
|
: parser(ctx),
|
|
2281
3259
|
);
|
|
2282
3260
|
}
|
|
@@ -2355,7 +3333,10 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
2355
3333
|
* */
|
|
2356
3334
|
use(
|
|
2357
3335
|
async function useValidateResponse(ctx, next) {
|
|
2358
|
-
const variants =
|
|
3336
|
+
const variants =
|
|
3337
|
+
validationSchemas.response?.[
|
|
3338
|
+
ctx.method === "HEAD" ? "GET" : ctx.method
|
|
3339
|
+
] || [];
|
|
2359
3340
|
|
|
2360
3341
|
if (!Array.isArray(variants) || !variants.length) {
|
|
2361
3342
|
return next();
|
|
@@ -2364,13 +3345,13 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
2364
3345
|
// options are same for all variants
|
|
2365
3346
|
const { runtimeValidation, customErrors } = variants[0];
|
|
2366
3347
|
|
|
2367
|
-
if (
|
|
2368
|
-
// skip if undefined or explicitly set to false
|
|
3348
|
+
if (command === "build") {
|
|
3349
|
+
// production build - skip if undefined or explicitly set to false
|
|
2369
3350
|
if (runtimeValidation === undefined || runtimeValidation === false) {
|
|
2370
3351
|
return next();
|
|
2371
3352
|
}
|
|
2372
3353
|
} else {
|
|
2373
|
-
// skip only if explicitly set to false
|
|
3354
|
+
// dev mode - skip only if explicitly set to false
|
|
2374
3355
|
if (runtimeValidation === false) {
|
|
2375
3356
|
return next();
|
|
2376
3357
|
}
|
|
@@ -2389,6 +3370,11 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
2389
3370
|
contentType: ctx.type,
|
|
2390
3371
|
};
|
|
2391
3372
|
|
|
3373
|
+
// validate only 2xx responses
|
|
3374
|
+
if (Math.floor(response.status / 100) !== 2) {
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
2392
3378
|
// Validate body only for JSON variants
|
|
2393
3379
|
if (variants.some((e) => e.contentType?.includes("json"))) {
|
|
2394
3380
|
response.body = ctx.body;
|
|
@@ -2527,7 +3513,9 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
2527
3513
|
use(
|
|
2528
3514
|
async (ctx, next) => {
|
|
2529
3515
|
const schema = {
|
|
2530
|
-
...validationSchemas[target]?.[
|
|
3516
|
+
...validationSchemas[target]?.[
|
|
3517
|
+
ctx.method === "HEAD" ? "GET" : ctx.method
|
|
3518
|
+
],
|
|
2531
3519
|
};
|
|
2532
3520
|
if (schema.validate && schema.runtimeValidation !== false) {
|
|
2533
3521
|
schema.validate(await loadData(ctx as never));
|
|
@@ -2557,7 +3545,7 @@ export const routes = createRoutes<ParameterizedMiddleware, RouterMiddleware>(
|
|
|
2557
3545
|
createRouteMiddleware,
|
|
2558
3546
|
},
|
|
2559
3547
|
);
|
|
2560
|
-
`,
|
|
3548
|
+
`,wt=`import { join } from "node:path";
|
|
2561
3549
|
|
|
2562
3550
|
import type { RouteSource } from "@kosmojs/core/api";
|
|
2563
3551
|
|
|
@@ -2609,11 +3597,23 @@ export const routeSources: Array<RouteSource<never>> = [
|
|
|
2609
3597
|
},
|
|
2610
3598
|
{{/each}}
|
|
2611
3599
|
];
|
|
2612
|
-
`,
|
|
3600
|
+
`,Tt=`import { chmod, unlink } from "node:fs/promises";
|
|
3601
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2613
3602
|
import { parseArgs, styleText } from "node:util";
|
|
2614
3603
|
|
|
2615
3604
|
import type { App } from "./app";
|
|
2616
3605
|
|
|
3606
|
+
export type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;
|
|
3607
|
+
|
|
3608
|
+
/**
|
|
3609
|
+
* Wrap the app into a node:http request listener.
|
|
3610
|
+
* Used by dist/run.js to mount this folder's API next to other folders;
|
|
3611
|
+
* the standalone server (\`serve\`) calls app.listen() directly instead.
|
|
3612
|
+
* */
|
|
3613
|
+
export const createListener = <T extends App>(app: T): NodeListener => {
|
|
3614
|
+
return app.callback();
|
|
3615
|
+
};
|
|
3616
|
+
|
|
2617
3617
|
type Handles = {
|
|
2618
3618
|
port?: number | undefined;
|
|
2619
3619
|
sock?: string | undefined;
|
|
@@ -2672,7 +3672,7 @@ export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
|
2672
3672
|
const server = app.listen(port || sock, onListen);
|
|
2673
3673
|
return server as never;
|
|
2674
3674
|
};
|
|
2675
|
-
`,
|
|
3675
|
+
`,Et=`import type { RouterContext } from "@koa/router";
|
|
2676
3676
|
import type { Next } from "koa";
|
|
2677
3677
|
|
|
2678
3678
|
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
@@ -2834,25 +3834,20 @@ export const defineRoute: <
|
|
|
2834
3834
|
use: use as never,
|
|
2835
3835
|
});
|
|
2836
3836
|
};
|
|
2837
|
-
`,
|
|
3837
|
+
`,Dt=`export * from "./@api/app";
|
|
2838
3838
|
export { appFactory as default } from "./@api/app";
|
|
2839
3839
|
export * from "./@api/dev";
|
|
2840
3840
|
export * from "./@api/errors";
|
|
2841
3841
|
export * from "./@api/router";
|
|
2842
3842
|
export * from "./@api/routes";
|
|
2843
3843
|
export * from "./@api/server";
|
|
2844
|
-
`,
|
|
3844
|
+
`,Ot=`import appFactory, { routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2845
3845
|
import defaultErrorHandler from "./errors";
|
|
2846
3846
|
|
|
2847
|
-
export default appFactory(routes, ({ app
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
// NOTE: Routes should be added last, after any middleware
|
|
2852
|
-
app.use(router.routes());
|
|
2853
|
-
|
|
2854
|
-
});
|
|
2855
|
-
`,at=`import app from "./app";
|
|
3847
|
+
export default appFactory(routes, ({ app }) => {
|
|
3848
|
+
app.use(defaultErrorHandler);
|
|
3849
|
+
})
|
|
3850
|
+
`,kt=`import app from "./app";
|
|
2856
3851
|
|
|
2857
3852
|
import { devSetup } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2858
3853
|
|
|
@@ -2870,39 +3865,43 @@ process.on("unhandledRejection", (reason) => {
|
|
|
2870
3865
|
console.error("Reason:", reason);
|
|
2871
3866
|
process.exit(1);
|
|
2872
3867
|
});
|
|
2873
|
-
`,
|
|
3868
|
+
`,At=`export declare module "{{ createImport 'libApi' }}" {
|
|
2874
3869
|
interface DefaultState {}
|
|
2875
3870
|
interface DefaultContext {}
|
|
2876
3871
|
}
|
|
2877
|
-
`,
|
|
3872
|
+
`,jt=`import { HTTPError, ValidationError } from "@kosmojs/core/errors";
|
|
2878
3873
|
|
|
2879
3874
|
import { errorHandlerFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2880
3875
|
|
|
2881
|
-
export default errorHandlerFactory(async (
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
ctx.
|
|
2894
|
-
|
|
2895
|
-
ctx.
|
|
3876
|
+
export default errorHandlerFactory(async (ctx, next) => {
|
|
3877
|
+
try {
|
|
3878
|
+
await next();
|
|
3879
|
+
} catch (error: any) {
|
|
3880
|
+
const [status, message] = Array.isArray(error)
|
|
3881
|
+
? error
|
|
3882
|
+
: error instanceof HTTPError
|
|
3883
|
+
? [error.status, error.message]
|
|
3884
|
+
: error instanceof ValidationError
|
|
3885
|
+
? [400, \`\${error.target}: \${error.errorMessage}\`]
|
|
3886
|
+
: [error.statusCode || 500, error.message];
|
|
3887
|
+
|
|
3888
|
+
ctx.status = status;
|
|
3889
|
+
|
|
3890
|
+
if (ctx.accepts("json")) {
|
|
3891
|
+
ctx.body = { error: message };
|
|
3892
|
+
} else {
|
|
3893
|
+
ctx.body = message;
|
|
3894
|
+
}
|
|
2896
3895
|
}
|
|
2897
3896
|
});
|
|
2898
|
-
`,
|
|
3897
|
+
`,Mt=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
2899
3898
|
|
|
2900
3899
|
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
2901
3900
|
GET(async (ctx) => {
|
|
2902
3901
|
ctx.body = "Automatically generated route";
|
|
2903
3902
|
}),
|
|
2904
3903
|
]);
|
|
2905
|
-
`,
|
|
3904
|
+
`,Nt=`import { use } from "{{ createImport 'libApi' }}";
|
|
2906
3905
|
|
|
2907
3906
|
export type UseT = {};
|
|
2908
3907
|
|
|
@@ -2913,11 +3912,11 @@ export default [
|
|
|
2913
3912
|
return next();
|
|
2914
3913
|
}),
|
|
2915
3914
|
];
|
|
2916
|
-
`,
|
|
3915
|
+
`,Pt=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2917
3916
|
import app from "./app";
|
|
2918
3917
|
|
|
2919
3918
|
await serve(app);
|
|
2920
|
-
`,
|
|
3919
|
+
`,Ft=`import { use } from "{{ createImport 'libApi' }}";
|
|
2921
3920
|
|
|
2922
3921
|
/**
|
|
2923
3922
|
* Define global middleware applied to all routes.
|
|
@@ -2928,17 +3927,17 @@ export default [
|
|
|
2928
3927
|
return next();
|
|
2929
3928
|
}),
|
|
2930
3929
|
];
|
|
2931
|
-
`,
|
|
3930
|
+
`,It=v((e,n)=>{let{createPath:r,createImportHelpers:i}=S(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=w({helpers:{...i({origin:`lib`}),...A(),paramsDefaults({params:e}){return`[${e.schema.map(()=>`unknown?`).join(`, `)}]`},paramsMappings({params:e}){return`[${e.schema.map(({name:e,kind:t})=>`["${e}", unknown, ${t===`required`?`true`:`false`}]`).join(`, `)}]`},cascadingState({cascadingMiddleware:e}){return a(e.map(({id:e})=>`UseT${e}`))}}}),{renderToFile:s}=w({helpers:i({origin:`src`})}),c=e=>e?.trim().length===0,u=l(n?.templates,Mt),d=async e=>{for(let{kind:t,entry:n}of e)t===`apiRoute`?await s(r.api(n.file),u(n.name,n),{route:n},{overwrite:c}):t===`apiUse`&&await s(r.api(n.file),Nt,{},{overwrite:c})},f=async e=>{let i=e.flatMap(({kind:e,entry:t})=>e===`apiUse`?[t]:[]),a=e.flatMap(({kind:e,entry:r})=>{if(e!==`apiRoute`)return[];let a=r.name.split(`/`).reduce((e,n)=>{let r=e[e.length-1];return e.push(r?t(r,n):n),e},[]),o={...r,basename:r.name,path:r.pathPattern,cascadingMiddleware:i.flatMap(e=>a.some(t=>e.name===t)?[e]:[])};return[o,...Object.entries({...n?.alias}).flatMap(([e,t])=>{let n=C(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${j(e)}`,alias:m(n),pathTokens:n}]:[]})]}).sort(E);for(let[e,t]of[[`@api/routes.ts`,wt]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{async start(){for(let[e,t]of[[`api.ts`,Et],[`api:factory.ts`,Dt],[`@api/app.ts`,vt],[`@api/dev.ts`,yt],[`@api/errors.ts`,bt],[`@api/listener.ts`,xt],[`@api/parsers.ts`,St],[`@api/router.ts`,Ct],[`@api/server.ts`,Tt]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,Ot],[`dev.ts`,kt],[`errors.ts`,jt],[`server.ts`,Pt],[`use.ts`,Ft],[`env.d.ts`,At]])await s(r.api(e),t,{},{overwrite:c})},async watch(e,t){await d(e.filter(h(t,[`create`]))),await f(e)},async build(e){await d(e),await f(e)}}}),Lt=_({meta:{name:`Koa`,slot:`backend`,types:[`@types/koa`]},dependencies:{koa:H.devDependencies.koa,"@koa/router":H.devDependencies[`@koa/router`],formidable:H.devDependencies.formidable,"raw-body":H.devDependencies[`raw-body`]},devDependencies:{"@types/koa":H.devDependencies[`@types/koa`],"@types/formidable":H.devDependencies[`@types/formidable`]},factory:It}),U={type:`module`,private:!0,name:`@kosmojs/mdx-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,"@mdx-js/rollup":`^3.1.1`,vite:`^8.2.2`},devDependencies:{"@mdx-js/mdx":`^3.1.1`,"@mdx-js/preact":`^3.1.1`,"path-to-regexp":`^8.4.2`,preact:`^10.29.8`,"preact-render-to-string":`^6.7.0`,"remark-frontmatter":`^5.0.0`,"remark-mdx-frontmatter":`^5.2.0`}},Rt=()=>{let e=[`🎉 Well done! You just created a new MDX page.`,`🚀 Success! A fresh MDX page is ready to roll.`,`🌟 Nice work! Another MDX page added to your site.`,`🧩 All set! A new MDX page has been scaffolded.`,`🔧 Scaffold complete! Your new MDX page is in place.`,`✅ Built! Your MDX page is scaffolded and ready.`,`✨ Fantastic! Your new MDX page is good to go.`,`🎯 Nailed it! A brand new MDX page just landed.`,`💫 Awesome! Another MDX page joins the party.`,`⚡ Lightning fast! A new MDX page created successfully.`];return e[Math.floor(Math.random()*e.length)]},zt=(e,t,n)=>{let{remarkPlugins:r=[],rehypePlugins:i=[]}={...n},a=()=>{let t=[`${u.srcDir}/${e.name}/${u.entryDir}/client.ts`].map(e=>re(e)),n=e=>t.some(t=>t(e));return{name:`kosmo:mdx[hmr]`,enforce:`post`,transform(e,t){if(!(!n(t)||e.includes(`import.meta.hot.accept`)))return{code:[e,`
|
|
2932
3931
|
if (import.meta.hot) {
|
|
2933
3932
|
import.meta.hot.accept(() => {});
|
|
2934
3933
|
}
|
|
2935
3934
|
`].join(`
|
|
2936
|
-
`)}}}},o=[ne({jsxImportSource:`preact`,providerImportSource:`@mdx-js/preact`,remarkPlugins:r,rehypePlugins:i})];return t===`serve`&&o.push(a()),o},
|
|
3935
|
+
`)}}}},o=[ne({jsxImportSource:`preact`,providerImportSource:`@mdx-js/preact`,remarkPlugins:r,rehypePlugins:i})];return t===`serve`&&o.push(a()),o},Bt=`import type { FunctionComponent } from "preact";
|
|
2937
3936
|
|
|
2938
3937
|
export const AppProvider: FunctionComponent = (props) => {
|
|
2939
3938
|
return props.children;
|
|
2940
3939
|
};
|
|
2941
|
-
`,
|
|
3940
|
+
`,Vt=`import { render, hydrate as hydrateOrig } from "preact";
|
|
2942
3941
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
2943
3942
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
2944
3943
|
|
|
@@ -2979,7 +3978,7 @@ export const mount = async (
|
|
|
2979
3978
|
}
|
|
2980
3979
|
|
|
2981
3980
|
export default clientRenderFactory();
|
|
2982
|
-
`,
|
|
3981
|
+
`,Ht=`import { renderToString as renderToStringOrig } from "preact-render-to-string";
|
|
2983
3982
|
|
|
2984
3983
|
import type {
|
|
2985
3984
|
RenderToStringWrapper,
|
|
@@ -3053,7 +4052,7 @@ export const renderToString: RenderToStringWrapper<
|
|
|
3053
4052
|
}
|
|
3054
4053
|
|
|
3055
4054
|
export default serverRenderFactory<false>();
|
|
3056
|
-
`,
|
|
4055
|
+
`,Ut=`declare module "*.mdx" {
|
|
3057
4056
|
import type { ComponentType } from "preact";
|
|
3058
4057
|
export const frontmatter: Record<string, unknown>;
|
|
3059
4058
|
const component: ComponentType;
|
|
@@ -3066,7 +4065,7 @@ declare module "*.md" {
|
|
|
3066
4065
|
const component: ComponentType;
|
|
3067
4066
|
export default component;
|
|
3068
4067
|
}
|
|
3069
|
-
`,
|
|
4068
|
+
`,Wt=`import { MDXProvider } from "@mdx-js/preact";
|
|
3070
4069
|
import { match, pathToRegexp } from "path-to-regexp";
|
|
3071
4070
|
import { type ComponentType, createContext, h, type VNode } from "preact";
|
|
3072
4071
|
|
|
@@ -3077,7 +4076,6 @@ import { base } from "{{ createImport 'libCore' }}";
|
|
|
3077
4076
|
|
|
3078
4077
|
export type RawRoute = {
|
|
3079
4078
|
name: string;
|
|
3080
|
-
pathSegments: number | undefined;
|
|
3081
4079
|
regexp: RegExp;
|
|
3082
4080
|
extractParams: (path: string) => Route["params"];
|
|
3083
4081
|
loader: () => Promise<RouteModule>;
|
|
@@ -3145,23 +4143,20 @@ export const createRouter = (
|
|
|
3145
4143
|
return {
|
|
3146
4144
|
async resolve(url: URL = new URL(window.location.href)) {
|
|
3147
4145
|
const searchParams = parseSearchParams(url);
|
|
3148
|
-
const urlSegments = url.pathname.split("/").filter(Boolean).length;
|
|
3149
|
-
|
|
3150
|
-
// 1: use lightweight \`RegExp.test()\` on linear scan - no capture allocation
|
|
3151
|
-
const matchedRoutes = routes.filter(({ regexp }) => {
|
|
3152
|
-
return regexp.test(url.pathname);
|
|
3153
|
-
});
|
|
3154
4146
|
|
|
4147
|
+
// The routes array is generated pre-sorted by specificity
|
|
4148
|
+
// (static beats required beats optional beats splat, token by token),
|
|
4149
|
+
// the same ordering the SSR server registers routes in -
|
|
4150
|
+
// so the first pattern that matches IS the most specific one,
|
|
4151
|
+
// and CSR resolution stays consistent with SSR.
|
|
4152
|
+
// A route with optional parameters matches a range of segment counts,
|
|
4153
|
+
// which is why no segment-count heuristic can disambiguate here.
|
|
4154
|
+
// Lightweight \`RegExp.test()\` on linear scan - no capture allocation.
|
|
3155
4155
|
const matchedRoute =
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
: matchedRoutes.length === 1
|
|
3161
|
-
? matchedRoutes[0]
|
|
3162
|
-
: catchallRoute;
|
|
3163
|
-
|
|
3164
|
-
// 2: capture params only on matched route
|
|
4156
|
+
routes.find(({ regexp }) => {
|
|
4157
|
+
return regexp.test(url.pathname);
|
|
4158
|
+
}) || catchallRoute;
|
|
4159
|
+
|
|
3165
4160
|
const params = matchedRoute
|
|
3166
4161
|
? matchedRoute.extractParams(url.pathname)
|
|
3167
4162
|
: {};
|
|
@@ -3255,11 +4250,6 @@ export const createRoute = (
|
|
|
3255
4250
|
return {
|
|
3256
4251
|
name,
|
|
3257
4252
|
regexp,
|
|
3258
|
-
// count segments of the same base-joined path the regexp matches against;
|
|
3259
|
-
// resolve() compares this against the full url pathname's segment count
|
|
3260
|
-
pathSegments: name.includes("...")
|
|
3261
|
-
? undefined
|
|
3262
|
-
: path.split("/").filter(Boolean).length,
|
|
3263
4253
|
extractParams: (path) => {
|
|
3264
4254
|
const match = matcher(path);
|
|
3265
4255
|
return match ? match.params : {};
|
|
@@ -3268,7 +4258,7 @@ export const createRoute = (
|
|
|
3268
4258
|
layouts,
|
|
3269
4259
|
};
|
|
3270
4260
|
};
|
|
3271
|
-
`,
|
|
4261
|
+
`,Gt=`/* @jsxImportSource preact */
|
|
3272
4262
|
|
|
3273
4263
|
import styles from "./styles.module.css";
|
|
3274
4264
|
|
|
@@ -3309,7 +4299,7 @@ export default function PageSample(props: {
|
|
|
3309
4299
|
</div>
|
|
3310
4300
|
);
|
|
3311
4301
|
}
|
|
3312
|
-
`,
|
|
4302
|
+
`,Kt=`/* @jsxImportSource preact */
|
|
3313
4303
|
|
|
3314
4304
|
import styles from "./styles.module.css";
|
|
3315
4305
|
|
|
@@ -3361,7 +4351,7 @@ export default function PageSample(props: {
|
|
|
3361
4351
|
</div>
|
|
3362
4352
|
);
|
|
3363
4353
|
}
|
|
3364
|
-
`,
|
|
4354
|
+
`,qt=`* {
|
|
3365
4355
|
margin: 0;
|
|
3366
4356
|
padding: 0;
|
|
3367
4357
|
box-sizing: border-box;
|
|
@@ -3496,7 +4486,7 @@ export default function PageSample(props: {
|
|
|
3496
4486
|
align-items: center;
|
|
3497
4487
|
gap: 0.25rem;
|
|
3498
4488
|
}
|
|
3499
|
-
`,
|
|
4489
|
+
`,Jt=`/* @jsxImportSource preact */
|
|
3500
4490
|
|
|
3501
4491
|
import styles from "./styles.module.css";
|
|
3502
4492
|
|
|
@@ -3562,7 +4552,7 @@ export default function WelcomePage() {
|
|
|
3562
4552
|
</div>
|
|
3563
4553
|
);
|
|
3564
4554
|
}
|
|
3565
|
-
`,
|
|
4555
|
+
`,Yt=`export type ParamsMap = {
|
|
3566
4556
|
{{#each pageRoutes}}"{{name}}": {{serializeParamsLiteral .}};
|
|
3567
4557
|
{{/each}}
|
|
3568
4558
|
};
|
|
@@ -3571,7 +4561,7 @@ export const paramNames = {
|
|
|
3571
4561
|
{{#each pageRoutes}}"{{name}}": [ {{#each params.schema}}"{{name}}", {{/each}}],
|
|
3572
4562
|
{{/each}}
|
|
3573
4563
|
} as const;
|
|
3574
|
-
`,
|
|
4564
|
+
`,Xt=`import type { ComponentType } from "preact";
|
|
3575
4565
|
|
|
3576
4566
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
3577
4567
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
@@ -3585,107 +4575,37 @@ import {
|
|
|
3585
4575
|
|
|
3586
4576
|
export const createRouters = (
|
|
3587
4577
|
routes: Array<RawRoute>,
|
|
3588
|
-
{
|
|
3589
|
-
app,
|
|
3590
|
-
components,
|
|
3591
|
-
}: {
|
|
3592
|
-
app: ComponentType;
|
|
3593
|
-
components: Record<string, ComponentType<never>>;
|
|
3594
|
-
},
|
|
3595
|
-
): {
|
|
3596
|
-
clientRouter: () => RouterFactoryReturn<Promise<RouteComponent>>;
|
|
3597
|
-
serverRouter: (
|
|
3598
|
-
url: URL,
|
|
3599
|
-
) => RouterFactoryReturn<Promise<RouteComponent>, { route: Route }>;
|
|
3600
|
-
} => {
|
|
3601
|
-
const router = createRouter(routes, app, { components });
|
|
3602
|
-
|
|
3603
|
-
return {
|
|
3604
|
-
clientRouter() {
|
|
3605
|
-
return router.resolve();
|
|
3606
|
-
},
|
|
3607
|
-
serverRouter(url) {
|
|
3608
|
-
return router.resolve(url);
|
|
3609
|
-
},
|
|
3610
|
-
};
|
|
3611
|
-
};
|
|
3612
|
-
|
|
3613
|
-
export default createRouterFactory<
|
|
3614
|
-
RawRoute,
|
|
3615
|
-
Promise<RouteComponent>,
|
|
3616
|
-
{ server: { route: Route } }
|
|
3617
|
-
>();
|
|
3618
|
-
`,Dt=`import { join } from "node:path";
|
|
3619
|
-
|
|
3620
|
-
import { compile } from "path-to-regexp";
|
|
3621
|
-
|
|
3622
|
-
import type { PageRoute } from "@kosmojs/core";
|
|
3623
|
-
|
|
3624
|
-
import routes from "{{ createImport 'lib' 'ssg:routes' }}";
|
|
3625
|
-
import { base } from "{{ createImport 'libCore' }}";
|
|
3626
|
-
|
|
3627
|
-
const paramsMapper = (params: PageRoute["params"], value: Array<unknown>) => {
|
|
3628
|
-
return params.schema.reduce<Record<string, unknown>>(
|
|
3629
|
-
(map, { name, kind }, i) => {
|
|
3630
|
-
if (kind === "splat") {
|
|
3631
|
-
if (Array.isArray(value[i]) && value[i].length) {
|
|
3632
|
-
map[name] = value[i].map(String);
|
|
3633
|
-
}
|
|
3634
|
-
} else if (value[i] !== undefined) {
|
|
3635
|
-
map[name] = String(value[i]);
|
|
3636
|
-
}
|
|
3637
|
-
return map;
|
|
3638
|
-
},
|
|
3639
|
-
{},
|
|
3640
|
-
);
|
|
3641
|
-
};
|
|
3642
|
-
|
|
3643
|
-
export default Object.entries(routes)
|
|
3644
|
-
.flatMap(([name, { pathPattern, params, frontmatter }]) => {
|
|
3645
|
-
if (!params.schema.length || Array.isArray(frontmatter?.staticParams)) {
|
|
3646
|
-
if (params.schema.length) {
|
|
3647
|
-
const toPath = compile(pathPattern);
|
|
3648
|
-
return Array.from(frontmatter?.staticParams || []).flatMap((entry) => {
|
|
3649
|
-
try {
|
|
3650
|
-
return [toPath(paramsMapper(params, entry) as never)];
|
|
3651
|
-
} catch (error: any) {
|
|
3652
|
-
console.error(\`❗SSG: Failed building path for \${name}\`);
|
|
3653
|
-
console.error(error);
|
|
3654
|
-
return [];
|
|
3655
|
-
}
|
|
3656
|
-
});
|
|
3657
|
-
}
|
|
3658
|
-
// static route
|
|
3659
|
-
return [pathPattern.replace(/^index\\/?/, "")];
|
|
3660
|
-
}
|
|
3661
|
-
return [];
|
|
3662
|
-
})
|
|
3663
|
-
.map((path) => join(base, path));
|
|
3664
|
-
`,Ot=`import type { PageRoute } from "@kosmojs/core";
|
|
3665
|
-
|
|
3666
|
-
{{#each pageRoutes}}
|
|
3667
|
-
import * as {{id}} from "{{ createImport 'pages' file }}";
|
|
3668
|
-
{{/each}}
|
|
3669
|
-
|
|
3670
|
-
const routeMap: Record<
|
|
3671
|
-
string,
|
|
3672
|
-
{
|
|
3673
|
-
frontmatter?: { staticParams?: Array<Array<string | Array<string>>> };
|
|
3674
|
-
pathPattern: string;
|
|
3675
|
-
params: PageRoute["params"];
|
|
3676
|
-
}
|
|
3677
|
-
> = {
|
|
3678
|
-
{{#each pageRoutes}}
|
|
3679
|
-
"{{name}}": {
|
|
3680
|
-
frontmatter: {{id}}.frontmatter,
|
|
3681
|
-
pathPattern: "{{pathPattern}}",
|
|
3682
|
-
params: {{serializeParams .}},
|
|
4578
|
+
{
|
|
4579
|
+
app,
|
|
4580
|
+
components,
|
|
4581
|
+
}: {
|
|
4582
|
+
app: ComponentType;
|
|
4583
|
+
components: Record<string, ComponentType<never>>;
|
|
3683
4584
|
},
|
|
3684
|
-
{
|
|
3685
|
-
|
|
4585
|
+
): {
|
|
4586
|
+
clientRouter: () => RouterFactoryReturn<Promise<RouteComponent>>;
|
|
4587
|
+
serverRouter: (
|
|
4588
|
+
url: URL,
|
|
4589
|
+
) => RouterFactoryReturn<Promise<RouteComponent>, { route: Route }>;
|
|
4590
|
+
} => {
|
|
4591
|
+
const router = createRouter(routes, app, { components });
|
|
3686
4592
|
|
|
3687
|
-
|
|
3688
|
-
|
|
4593
|
+
return {
|
|
4594
|
+
clientRouter() {
|
|
4595
|
+
return router.resolve();
|
|
4596
|
+
},
|
|
4597
|
+
serverRouter(url) {
|
|
4598
|
+
return router.resolve(url);
|
|
4599
|
+
},
|
|
4600
|
+
};
|
|
4601
|
+
};
|
|
4602
|
+
|
|
4603
|
+
export default createRouterFactory<
|
|
4604
|
+
RawRoute,
|
|
4605
|
+
Promise<RouteComponent>,
|
|
4606
|
+
{ server: { route: Route } }
|
|
4607
|
+
>();
|
|
4608
|
+
`,Zt=`import { useContext } from "preact/hooks";
|
|
3689
4609
|
|
|
3690
4610
|
import { RouterContext } from "./mdx";
|
|
3691
4611
|
|
|
@@ -3731,10 +4651,10 @@ export const useFrontmatter = <
|
|
|
3731
4651
|
>(): T => {
|
|
3732
4652
|
return useRoute().frontmatter as T;
|
|
3733
4653
|
};
|
|
3734
|
-
`,
|
|
4654
|
+
`,Qt=`import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
3735
4655
|
|
|
3736
4656
|
<AppProvider>{props.children}</AppProvider>
|
|
3737
|
-
|
|
4657
|
+
`,$t=`import { h, type JSX } from "preact";
|
|
3738
4658
|
|
|
3739
4659
|
import { pageRouteMap, type LinkProps } from "{{ createImport 'libCore' }}";
|
|
3740
4660
|
|
|
@@ -3751,7 +4671,7 @@ export default function Link(
|
|
|
3751
4671
|
|
|
3752
4672
|
return h("a", { ...restProps, href }, children);
|
|
3753
4673
|
}
|
|
3754
|
-
`,
|
|
4674
|
+
`,en=`/**
|
|
3755
4675
|
* MDX component overrides.
|
|
3756
4676
|
*
|
|
3757
4677
|
* Every standard markdown element (headings, links, code blocks, etc.)
|
|
@@ -3774,7 +4694,7 @@ export const components = {
|
|
|
3774
4694
|
declare global {
|
|
3775
4695
|
type MDXProvidedComponents = typeof components;
|
|
3776
4696
|
}
|
|
3777
|
-
`,
|
|
4697
|
+
`,tn=`import renderFactory, {
|
|
3778
4698
|
createRoutes,
|
|
3779
4699
|
hydrate,
|
|
3780
4700
|
mount,
|
|
@@ -3801,7 +4721,7 @@ if (root) {
|
|
|
3801
4721
|
} else {
|
|
3802
4722
|
console.error("❌ Root element not found!");
|
|
3803
4723
|
}
|
|
3804
|
-
`,
|
|
4724
|
+
`,nn=`import renderFactory, {
|
|
3805
4725
|
createRoutes,
|
|
3806
4726
|
renderToString,
|
|
3807
4727
|
// no renderToStream on MDX folders
|
|
@@ -3822,25 +4742,13 @@ export default renderFactory(() => {
|
|
|
3822
4742
|
},
|
|
3823
4743
|
};
|
|
3824
4744
|
});
|
|
3825
|
-
`,
|
|
3826
|
-
<html lang="en">
|
|
3827
|
-
<head>
|
|
3828
|
-
<meta charset="UTF-8" />
|
|
3829
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3830
|
-
<!--app-head-->
|
|
3831
|
-
</head>
|
|
3832
|
-
<body>
|
|
3833
|
-
<div id="app"><!--app-html--></div>
|
|
3834
|
-
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
3835
|
-
</body>
|
|
3836
|
-
</html>
|
|
3837
|
-
`,It=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
4745
|
+
`,rn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
3838
4746
|
|
|
3839
4747
|
export default function Page() {
|
|
3840
4748
|
return <PageSample />;
|
|
3841
4749
|
}
|
|
3842
|
-
`,
|
|
3843
|
-
`,
|
|
4750
|
+
`,an=`{props.children}
|
|
4751
|
+
`,on=`---
|
|
3844
4752
|
title: "{{title}}"
|
|
3845
4753
|
---
|
|
3846
4754
|
|
|
@@ -3857,7 +4765,7 @@ export const pathMap = {
|
|
|
3857
4765
|
routeName="{{route.name}}"
|
|
3858
4766
|
pathMap={pathMap}
|
|
3859
4767
|
/>
|
|
3860
|
-
`,
|
|
4768
|
+
`,sn=`---
|
|
3861
4769
|
title: Welcome to KosmoJS
|
|
3862
4770
|
description: Content-first development with MDX and Vite
|
|
3863
4771
|
---
|
|
@@ -3865,7 +4773,7 @@ description: Content-first development with MDX and Vite
|
|
|
3865
4773
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}"
|
|
3866
4774
|
|
|
3867
4775
|
<WelcomePage />
|
|
3868
|
-
`,
|
|
4776
|
+
`,cn=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
3869
4777
|
|
|
3870
4778
|
import app from "./app.mdx";
|
|
3871
4779
|
import { components } from "./components/mdx"
|
|
@@ -3881,12 +4789,12 @@ export default routerFactory((routes) => {
|
|
|
3881
4789
|
},
|
|
3882
4790
|
};
|
|
3883
4791
|
});
|
|
3884
|
-
`,
|
|
4792
|
+
`,ln=v((e,t)=>{let{createPath:n,createImportHelpers:r}=S(e),{renderToFile:i}=w({helpers:{...r({origin:`lib`}),...A(),serializeParams(e){return JSON.stringify(e.params)}}}),{renderToFile:a}=w({helpers:r({origin:`src`})}),o=e=>!e?.trim().length,s=l(t?.templates,on),c=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await a(n.pages(r.file),r.name===`index`?sn:s(r.name,r),{route:r,title:r.name.replace(/\{([^}]+)\}/g,`$1`),message:Rt()},{overwrite:o}):t===`pageLayout`&&await a(n.pages(r.file),an,{route:r},{overwrite:o})},d=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageLayout`?[t]:[]),r=e.flatMap(({kind:e,entry:n})=>{if(e===`pageRoute`){let{name:e,file:r}=n;return[{...n,layouts:t.flatMap(t=>t.name===e||r.startsWith(`${t.name}/`)?[t]:[]).sort(E)}]}return[]}).sort(D);for(let[e,a]of[[`client.ts`,Vt],[`server.ts`,Ht]])await i(n.libEntry(e),a,{pageRoutes:r,layouts:t});for(let[e,t]of[[`params.ts`,Yt],[`router.ts`,Xt]])await i(n.lib(e),t,{pageRoutes:r})};return{config({command:n}){return{oxc:{jsx:{runtime:`automatic`,importSource:`preact`}},plugins:zt(e,n,t)}},async start(){for(let[e,t]of[[`env.d.ts`,Ut],[`app.ts`,Bt],[`mdx.ts`,Wt],[`use.ts`,Zt],[`pageSamples/styles.module.css`,qt],[`pageSamples/welcome.tsx`,Jt],[`pageSamples/page.tsx`,Kt],[`pageSamples/404.tsx`,Gt]])await i(n.lib(e),t,{});for(let[e,t]of[[`pages/404.mdx`,rn],[`components/Link.tsx`,$t],[`components/mdx.ts`,en],[`app.mdx`,Qt],[`router.ts`,cn]])await a(n.src(e),t,{entryDir:u.entryDir},{overwrite:o});for(let[e,t]of[[`client.ts`,tn],[`server.ts`,nn]])await a(n.entry(e),t,{},{overwrite:o})},async watch(e,t){await c(e.filter(g(t,[`create`]))),await d(e)},async build(e){await c(e),await d(e)}}}),un=_({meta:{name:`MDX`,slot:`frontend`,jsx:`react-jsx`,jsxImportSource:`preact`},dependencies:{"path-to-regexp":U.devDependencies[`path-to-regexp`]},devDependencies:{preact:U.devDependencies.preact,"preact-render-to-string":U.devDependencies[`preact-render-to-string`],"@mdx-js/preact":U.devDependencies[`@mdx-js/preact`],"remark-frontmatter":U.devDependencies[`remark-frontmatter`],"remark-mdx-frontmatter":U.devDependencies[`remark-mdx-frontmatter`]},factory:ln}),dn={json:`application/json`,form:[`application/x-www-form-urlencoded`,`multipart/form-data`],raw:void 0},fn=()=>{let e=e=>[`Buffer`,`ArrayBuffer`,`Blob`].includes(e)?{type:`string`,format:`binary`}:oe.Script(e),n=(e,t,n)=>{let r=n?`${t}_${n.replace(/[^\w.-]/g,`_`)}${j(n)}`:t;return`${e.id}_${r}`},r=(e,t,r,i)=>[`#`,`components`,e,n(t,r,i)].join(`/`),i=e=>e.split(`/`).reduce((e,t)=>e+ +!t.includes(`{`),0),a=e=>{if(e.name===`index`)return[`/`];let{tokens:n}=ae(e.pathPattern),r=e=>e.flatMap(e=>{switch(e.type){case`param`:return[e.name];case`wildcard`:return[e.name];case`group`:return r(e.tokens);default:return[]}}),a=(e,t)=>{if(!e.length)return[t];let[n,...i]=e;switch(n.type){case`text`:return a(i,{path:`${t.path}${n.value}`,params:t.params});case`param`:return a(i,{path:`${t.path}{${n.name}}`,params:[...t.params,n.name]});case`wildcard`:return a(i,{path:`${t.path}{${n.name}*}`,params:[...t.params,n.name]});case`group`:{let e=a(i,t),o=a([...n.tokens,...i],t),s=r(n.tokens),c=o.filter(e=>s.some(t=>e.params.includes(t)));return[...e,...c]}}},o=a(n,{path:``,params:[]}),s=e.params.schema.reduce((e,{name:t},n)=>(e[n]=t,e),{});return o.reduce((e,n)=>{let r=t(`/`,n.path);return e.includes(r)||n.params.every((e,t)=>e===s[t])&&e.push(r),e},[]).sort((e,t)=>i(t)-i(e))},s=(e,t)=>{let n=e.params.resolvedType?.properties?.flatMap(n=>RegExp(`\\{${n.name}\\*?\\}`).test(t)?[{$ref:r(`parameters`,e,e.params.id,n.name)}]:[]);return n?.length?n:void 0},c=(e,t)=>{let n=e.validationDefinitions.find(e=>e.method===t&&e.target===`response`);return Array.isArray(n?.variants)?n.variants.reduce((t,{id:n,status:i,contentType:a,body:o})=>(t[i]=pn(i,a)||{description:`Success`,content:{[a||`application/json`]:{schema:o?{$ref:r(`schemas`,e,n)}:{type:`object`}}}},t),{}):{200:{description:`Success - TODO: Add response schema`,content:{"text/plain":{schema:{type:`string`}}}}}},l=t=>{let{parameters:r,schemas:i}={parameters:{},schemas:{}};for(let r of t.validationDefinitions)if(r.target===`response`)for(let{id:a,resolvedType:o}of r.variants)o?.typeboxSchema&&(i[n(t,a)]=e(o.typeboxSchema));else if(r.target===`query`){let{id:a,resolvedType:o}=r.schema;for(let r of o?.properties||[])if(r?.typeboxSchema){let o=n(t,a,r.name);i[o]=e(r.typeboxSchema)}}else{let{id:a,resolvedType:o}=r.schema;o?.typeboxSchema&&(i[n(t,a)]=e(o.typeboxSchema))}if(t.params.resolvedType)for(let i of t.params.resolvedType.properties||[])i?.typeboxSchema&&(r[n(t,t.params.id,i.name)]={name:i.name,in:`path`,required:!0,schema:e(i.typeboxSchema)});return{parameters:r,schemas:i}},u=e=>{let t={},n=e.validationDefinitions.flatMap(e=>e.target===`response`?[]:e.schema.resolvedType?[e]:[]);for(let i of a(e))for(let a of e.methods){let l={responses:c(e,a)},u=s(e,i);u&&(l.parameters=u);let d=n.find(e=>e.method===a&&e.target===`query`);if(d?.schema)for(let t of d.schema.resolvedType?.properties||[])l.parameters||=[],l.parameters.push({name:t.name,in:`query`,required:!t.optional,schema:{$ref:r(`schemas`,e,d.schema.id,t.name)}});let f=n.filter(e=>e.method===a&&Object.keys(o).includes(e.target));f.length&&(l.requestBody={required:!0,content:f.reduce((t,n)=>{let{contentType:i=dn[n.target]}=n;if(i){let a={$ref:r(`schemas`,e,n.schema.id)};for(let e of[i].flat())t[e]={schema:a}}return t},{})}),t[i]||(t[i]={}),t[i][a.toLowerCase()]=l}return t};return{generateComponentId:n,generateComponentPath:r,generatePathVariations:a,generateOpenAPISchema:e=>{let t=new Map;for(let n of e)t.set(n.name,a(n));let{components:n,paths:r}=e.sort(E).flatMap(n=>{let r=t.get(n.name)??[];return r.length>0&&e.some(e=>{if(e.name===n.name)return!1;let i=t.get(e.name)??[],a=new Set(i);return r.every(e=>a.has(e))})?[]:[n]}).reduce((e,t)=>{let n=u(t),{parameters:r,schemas:i}=l(t);return{paths:{...e.paths,...n},components:{parameters:{...e.components.parameters,...r},schemas:{...e.components.schemas,...i}}}},{paths:{},components:{parameters:{},schemas:{}}});return{paths:r,components:n}}}},pn=(e,t)=>{let n={type:`string`,format:`uri`,...t?{enum:[t]}:{}};return{301:{description:`Moved Permanently`,headers:{Location:{description:`New permanent location`,schema:n}}},302:{description:`Found`,headers:{Location:{description:`Temporary location`,schema:n}}},303:{description:`See Other`,headers:{Location:{description:`Location to GET after POST/PUT/DELETE`,schema:n}}},307:{description:`Temporary Redirect`,headers:{Location:{description:`Temporary location (preserves request method)`,schema:n}}},308:{description:`Permanent Redirect`,headers:{Location:{description:`New permanent location (preserves request method)`,schema:n}}}}[e]},mn=v((e,t)=>{let{outfile:n=``,...r}={...t},{createPath:i}=S(e),{generateOpenAPISchema:a}=fn(),o=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]),{paths:o,components:s}=a(t),c={...JSON.parse(JSON.stringify(r)),paths:o,components:s},l=/ya?ml/.test(n)?ie.stringify(c):JSON.stringify(c,null,2);await T(i.src(n),l,{})};return{async watch(e){await o(e)},async build(e){await o(e)}}}),hn=_({meta:{name:`OpenAPI`,resolveTypes:!0},factory:mn}),W={type:`module`,private:!0,name:`@kosmojs/react-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/react-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,"@vitejs/plugin-react":`^6.1.0`},devDependencies:{"@tanstack/react-query":`^5.102.2`,"@types/react":`^19.2.18`,"@types/react-dom":`^19.2.5`,react:`^19.2.8`,"react-dom":`^19.2.8`,"react-router":`^8.3.0`}},gn=e=>{let t=e.map(e=>e.orig).join(`/`),n=e=>e.kind===`splat`?`*`:e.kind===`optional`?`:${e.name}?`:`:${e.name}`;return e.flatMap(e=>e.kind===`static`?[e.parts[0].value]:e.kind===`param`?[n(e.parts[0])]:(/\.\w+$/.test(e.orig)||(console.warn(`❗${i([`red`,`bold`],`WARN`)}: React Router v7 only supports dot-suffix mixed segments (e.g. :param.html).`),console.warn(` ${i([`magenta`],e.orig)} in ${i([`blue`],t)} route won't match as expected.`),console.warn()),[e.parts.map(e=>e.type===`static`?e.value:n(e)).join(``)])).join(`/`)},_n=()=>{let e=e=>e?.kind===`param`&&e.parts[0]?.kind===`splat`,t=n=>n.flatMap(({index:n,layout:r,children:i})=>{let{name:a,pathTokens:o}={...n,...r};if(!o)return[];let s=`${a}:layout`,c=gn(o),l=o.at(-1);return e(l)?n&&r?[{name:s,path:c,component:r.id,children:[{name:a,path:`*`,component:n.id}]}]:n?[{path:c,children:[{name:a,path:`*`,component:n.id},...t(i)]}]:r?[{name:s,path:c,component:r.id,children:t(i)}]:[]:n&&r?[{name:s,path:c,component:r.id,children:[{name:a,index:!0,component:n.id},...t(i)]}]:n?[{path:c,children:[{name:a,index:!0,component:n.id},...t(i)]}]:r?[{name:s,path:c,component:r.id,children:t(i)}]:[]});return t},vn=()=>{let e=[`🎉 Well done! You just created a new React route.`,`🚀 Success! A fresh React route is ready to roll.`,`🌟 Nice work! Another React route added to your app.`,`⚡ Quick and easy! Your new React route is good to go.`,`🥳 Congrats! Your app just leveled up with a new React route.`,`🧩 All set! A new React route has been scaffolded.`,`🔧 Scaffold complete! Your new React route is in place.`,`✨ Fantastic! Your new React route is ready.`,`🎯 Nailed it! A brand new React route just landed.`,`💫 Awesome! Another React route joins the lineup.`];return e[Math.floor(Math.random()*e.length)]},yn=`import type { ReactNode } from "react";
|
|
3885
4793
|
|
|
3886
4794
|
export const AppProvider = ({ children }: { children: ReactNode }) => {
|
|
3887
4795
|
return children;
|
|
3888
4796
|
}
|
|
3889
|
-
`,
|
|
4797
|
+
`,bn=`import { type QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3890
4798
|
import type { ReactNode } from "react";
|
|
3891
4799
|
|
|
3892
4800
|
import { getQueryClient } from "./query";
|
|
@@ -3905,7 +4813,7 @@ export const AppProvider = ({
|
|
|
3905
4813
|
</QueryClientProvider>
|
|
3906
4814
|
);
|
|
3907
4815
|
}
|
|
3908
|
-
|
|
4816
|
+
`,xn=`import { lazy, type JSX } from "react";
|
|
3909
4817
|
|
|
3910
4818
|
import {
|
|
3911
4819
|
createRoot,
|
|
@@ -3959,7 +4867,7 @@ export const mount = async (
|
|
|
3959
4867
|
}
|
|
3960
4868
|
|
|
3961
4869
|
export default clientRenderFactory();
|
|
3962
|
-
`,
|
|
4870
|
+
`,Sn=`{
|
|
3963
4871
|
{{#if name}}
|
|
3964
4872
|
id: "{{name}}",
|
|
3965
4873
|
{{/if}}
|
|
@@ -3977,7 +4885,7 @@ export default clientRenderFactory();
|
|
|
3977
4885
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
3978
4886
|
{{/if}}
|
|
3979
4887
|
}
|
|
3980
|
-
`,
|
|
4888
|
+
`,Cn=`import type { JSX } from "react";
|
|
3981
4889
|
|
|
3982
4890
|
import {
|
|
3983
4891
|
renderToString as renderToStringOrig,
|
|
@@ -4043,7 +4951,12 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
4043
4951
|
};
|
|
4044
4952
|
|
|
4045
4953
|
export default serverRenderFactory();
|
|
4046
|
-
`,
|
|
4954
|
+
`,wn=`declare module "virtual:kosmo/tsq-client" {
|
|
4955
|
+
import type { QueryClient, QueryClientConfig } from "@tanstack/react-query";
|
|
4956
|
+
export const createQueryClient: (options?: QueryClientConfig) => QueryClient;
|
|
4957
|
+
export const getQueryClient: () => QueryClient;
|
|
4958
|
+
}
|
|
4959
|
+
`,Tn=`/* @jsxImportSource react */
|
|
4047
4960
|
|
|
4048
4961
|
import styles from "./styles.module.css";
|
|
4049
4962
|
|
|
@@ -4084,7 +4997,7 @@ export default function PageSample(props: {
|
|
|
4084
4997
|
</div>
|
|
4085
4998
|
);
|
|
4086
4999
|
}
|
|
4087
|
-
`,
|
|
5000
|
+
`,En=`/* @jsxImportSource react */
|
|
4088
5001
|
|
|
4089
5002
|
import styles from "./styles.module.css";
|
|
4090
5003
|
|
|
@@ -4136,7 +5049,7 @@ export default function PageSample(props: {
|
|
|
4136
5049
|
</div>
|
|
4137
5050
|
);
|
|
4138
5051
|
}
|
|
4139
|
-
`,
|
|
5052
|
+
`,Dn=`* {
|
|
4140
5053
|
margin: 0;
|
|
4141
5054
|
padding: 0;
|
|
4142
5055
|
box-sizing: border-box;
|
|
@@ -4271,7 +5184,7 @@ export default function PageSample(props: {
|
|
|
4271
5184
|
align-items: center;
|
|
4272
5185
|
gap: 0.25rem;
|
|
4273
5186
|
}
|
|
4274
|
-
`,
|
|
5187
|
+
`,On=`/* @jsxImportSource react */
|
|
4275
5188
|
|
|
4276
5189
|
import styles from "./styles.module.css";
|
|
4277
5190
|
|
|
@@ -4337,26 +5250,27 @@ export default function WelcomePage() {
|
|
|
4337
5250
|
</div>
|
|
4338
5251
|
);
|
|
4339
5252
|
}
|
|
4340
|
-
`,
|
|
5253
|
+
`,kn=`export * from "virtual:kosmo/tsq-client";
|
|
5254
|
+
`,An=`import { QueryClient } from "@tanstack/react-query";
|
|
4341
5255
|
|
|
4342
|
-
let client
|
|
5256
|
+
let client = undefined;
|
|
4343
5257
|
|
|
4344
|
-
export const createQueryClient = (options
|
|
5258
|
+
export const createQueryClient = (options) => {
|
|
4345
5259
|
client = new QueryClient(options);
|
|
4346
5260
|
return client;
|
|
4347
5261
|
};
|
|
4348
5262
|
|
|
4349
|
-
export const getQueryClient = ()
|
|
5263
|
+
export const getQueryClient = () => {
|
|
4350
5264
|
if (!client) {
|
|
4351
5265
|
client = new QueryClient();
|
|
4352
5266
|
}
|
|
4353
5267
|
return client;
|
|
4354
5268
|
};
|
|
4355
|
-
`,
|
|
5269
|
+
`,jn=`import { QueryClient } from "@tanstack/react-query";
|
|
4356
5270
|
|
|
4357
|
-
import { store } from "{{ createImport '
|
|
5271
|
+
import { store } from "{{ createImport 'libCore' 'ssr' }}";
|
|
4358
5272
|
|
|
4359
|
-
export const createQueryClient = (options
|
|
5273
|
+
export const createQueryClient = (options) => {
|
|
4360
5274
|
const client = new QueryClient(options);
|
|
4361
5275
|
const ctx = store?.getStore();
|
|
4362
5276
|
if (ctx) {
|
|
@@ -4365,7 +5279,7 @@ export const createQueryClient = (options?: QueryClientConfig): QueryClient => {
|
|
|
4365
5279
|
return client;
|
|
4366
5280
|
};
|
|
4367
5281
|
|
|
4368
|
-
export const getQueryClient = ()
|
|
5282
|
+
export const getQueryClient = () => {
|
|
4369
5283
|
const ctx = store?.getStore();
|
|
4370
5284
|
if (!ctx) {
|
|
4371
5285
|
throw new Error("getQueryClient(): called outside an SSR request scope");
|
|
@@ -4373,9 +5287,9 @@ export const getQueryClient = (): QueryClient => {
|
|
|
4373
5287
|
if (!ctx.tsqClient) {
|
|
4374
5288
|
ctx.tsqClient = new QueryClient();
|
|
4375
5289
|
}
|
|
4376
|
-
return ctx.tsqClient
|
|
5290
|
+
return ctx.tsqClient;
|
|
4377
5291
|
};
|
|
4378
|
-
`,
|
|
5292
|
+
`,Mn=`export type ComponentLoader = () => Promise<{
|
|
4379
5293
|
loader?: (arg: unknown) => Promise<unknown>;
|
|
4380
5294
|
}>;
|
|
4381
5295
|
|
|
@@ -4395,7 +5309,7 @@ export const loaderFactory = (opt?: { withPreload?: boolean }) => {
|
|
|
4395
5309
|
return opt?.withPreload ? { loader } : {};
|
|
4396
5310
|
};
|
|
4397
5311
|
};
|
|
4398
|
-
`,
|
|
5312
|
+
`,Nn=`import type { JSX, ComponentType } from "react";
|
|
4399
5313
|
|
|
4400
5314
|
import {
|
|
4401
5315
|
type RouteObject,
|
|
@@ -4453,7 +5367,7 @@ export const createRouters = (
|
|
|
4453
5367
|
}
|
|
4454
5368
|
|
|
4455
5369
|
export default createRouterFactory<RouteObject, Promise<JSX.Element>>();
|
|
4456
|
-
`,
|
|
5370
|
+
`,Pn=`import { Outlet } from "react-router";
|
|
4457
5371
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
4458
5372
|
|
|
4459
5373
|
export default function App() {
|
|
@@ -4463,7 +5377,7 @@ export default function App() {
|
|
|
4463
5377
|
</AppProvider>
|
|
4464
5378
|
);
|
|
4465
5379
|
}
|
|
4466
|
-
`,
|
|
5380
|
+
`,Fn=`import {
|
|
4467
5381
|
type LinkProps as RouterLinkProps,
|
|
4468
5382
|
Link as RouterLink,
|
|
4469
5383
|
} from "react-router";
|
|
@@ -4491,7 +5405,7 @@ export default function Link(
|
|
|
4491
5405
|
</RouterLink>
|
|
4492
5406
|
);
|
|
4493
5407
|
}
|
|
4494
|
-
`,
|
|
5408
|
+
`,In=`import renderFactory, {
|
|
4495
5409
|
createRoutes,
|
|
4496
5410
|
hydrate,
|
|
4497
5411
|
mount,
|
|
@@ -4518,7 +5432,7 @@ if (root) {
|
|
|
4518
5432
|
} else {
|
|
4519
5433
|
console.error("❌ Root element not found!");
|
|
4520
5434
|
}
|
|
4521
|
-
`,
|
|
5435
|
+
`,Ln=`import renderFactory, {
|
|
4522
5436
|
createRoutes,
|
|
4523
5437
|
renderToStream,
|
|
4524
5438
|
renderToString,
|
|
@@ -4545,29 +5459,17 @@ export default renderFactory(() => {
|
|
|
4545
5459
|
},
|
|
4546
5460
|
};
|
|
4547
5461
|
});
|
|
4548
|
-
`,
|
|
4549
|
-
<html lang="en">
|
|
4550
|
-
<head>
|
|
4551
|
-
<meta charset="UTF-8" />
|
|
4552
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4553
|
-
<!--app-head-->
|
|
4554
|
-
</head>
|
|
4555
|
-
<body>
|
|
4556
|
-
<div id="app"><!--app-html--></div>
|
|
4557
|
-
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
4558
|
-
</body>
|
|
4559
|
-
</html>
|
|
4560
|
-
`,gn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
5462
|
+
`,Rn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
4561
5463
|
|
|
4562
5464
|
export default function Page() {
|
|
4563
5465
|
return <PageSample />;
|
|
4564
5466
|
}
|
|
4565
|
-
`,
|
|
5467
|
+
`,zn=`import { Outlet } from "react-router";
|
|
4566
5468
|
|
|
4567
5469
|
export default function Layout() {
|
|
4568
5470
|
return <Outlet />;
|
|
4569
5471
|
}
|
|
4570
|
-
`,
|
|
5472
|
+
`,Bn=`import PageSample from "{{ createImport 'lib' 'pageSamples/page.tsx' }}";
|
|
4571
5473
|
|
|
4572
5474
|
export default function Page() {
|
|
4573
5475
|
return PageSample({
|
|
@@ -4580,8 +5482,8 @@ export default function Page() {
|
|
|
4580
5482
|
},
|
|
4581
5483
|
});
|
|
4582
5484
|
}
|
|
4583
|
-
`,
|
|
4584
|
-
`,
|
|
5485
|
+
`,Vn=`export { default } from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}";
|
|
5486
|
+
`,Hn=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
4585
5487
|
|
|
4586
5488
|
import app from "./app";
|
|
4587
5489
|
|
|
@@ -4596,12 +5498,12 @@ export default routerFactory((routes) => {
|
|
|
4596
5498
|
},
|
|
4597
5499
|
};
|
|
4598
5500
|
});
|
|
4599
|
-
`,
|
|
5501
|
+
`,Un=v((e,t)=>{let{createPath:n,createImportHelpers:r}=S(e),{render:i,renderToFile:a}=w({helpers:{...r({origin:`lib`}),...A()},partials:{routePartial:Sn}}),{renderToFile:o}=w({helpers:r({origin:`src`})}),s=_n(),c=e=>!e?.trim().length,d=l(t?.templates,Bn),f=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await o(n.pages(r.file),r.name===`index`?Vn:d(r.name,r),{route:r,message:vn()},{overwrite:c}):t===`pageLayout`&&await o(n.pages(r.file),zn,{route:r},{overwrite:c})},p=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(E),r=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),i=s(b(r));for(let[e,t]of[[`client.ts`,xn],[`server.ts`,Cn]])await a(n.libEntry(e),t,{pageEntries:r,nestedRoutes:i});await a(n.lib(`router.tsx`),Nn,{entries:e,indexRoutes:t})};return{config(){let{templates:e,...n}={...t};return{plugins:[se(n)]}},async start(){for(let[e,r]of[[`env.d.ts`,wn],[`react.ts`,Mn],[`pageSamples/styles.module.css`,Dn],[`pageSamples/welcome.tsx`,On],[`pageSamples/page.tsx`,En],[`pageSamples/404.tsx`,Tn],...t?.tanstack?.query?[[`app.tsx`,bn],[`query.ts`,kn]]:[[`app.tsx`,yn],[`query.ts`,`/** tanstack query disabled */`]]])await a(n.lib(e),r,{});for(let[e,t]of[[`pages/404.tsx`,Rn],[`components/Link.tsx`,Fn],[`app.tsx`,Pn],[`router.ts`,Hn]])await o(n.src(e),t,{entryDir:u.entryDir},{overwrite:c});for(let[e,t]of[[`client.ts`,In],[`server.ts`,Ln]])await o(n.entry(e),t,{},{overwrite:c})},async watch(e,t){await f(e.filter(g(t,[`create`]))),await p(e)},async build(e){await f(e),await p(e)},virtualModules(){return t?.tanstack?.query?[{specifier:`virtual:kosmo/tsq-client`,csr:i(An,{}),ssr:i(jn,{})}]:[]}}}),Wn=_({meta:{name:`React`,slot:`frontend`,jsx:`preserve`,jsxImportSource:`react`},dependencies(e){return{react:W.devDependencies.react,"react-router":W.devDependencies[`react-router`],...e?.tanstack?.query?{"@tanstack/react-query":W.devDependencies[`@tanstack/react-query`]}:{}}},devDependencies:{"@types/react":W.devDependencies[`@types/react`],"@types/react-dom":W.devDependencies[`@types/react-dom`],"react-dom":W.devDependencies[`react-dom`]},factory:Un}),G={type:`module`,private:!0,name:`@kosmojs/solid-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/solid-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,"vite-plugin-solid":`^2.11.14`},devDependencies:{"@solidjs/router":`^1.0.0`,"@tanstack/solid-query":`^5.102.2`,"solid-js":`^1.9.15`}},Gn=()=>{let e=e=>e?.kind===`param`&&e.parts[0]?.kind===`splat`,t=t=>t?.kind===`param`?t.parts[0]?.kind===`optional`||e(t):!1,n=r=>r.flatMap(({index:r,layout:i,children:a})=>{let{pathTokens:o}={...r,...i},s=K(o.map(e=>e.kind===`param`&&a.some(e=>e.index?.pathTokens.some(t))?{...e,parts:[{...e.parts[0],kind:`required`}]}:e)),c=o.at(-1);if(e(c)){let e=K([c]);return r&&i?[{path:s,component:i.id,children:[{path:e,component:r.id}]}]:r?[{path:s,children:[{path:e,component:r.id},...n(a)]}]:i?[{path:s,component:i.id,children:n(a)}]:[]}return r&&i?[{path:s,component:i.id,children:[{path:`/`,component:r.id},...n(a)]}]:r?[{path:s,children:[{path:`/`,component:r.id},...n(a)]}]:i?[{path:s,component:i.id,children:n(a)}]:[]});return n},K=e=>{let t=e.map(e=>e.orig).join(`/`),n=e=>e.kind===`splat`?`*${e.name}`:e.kind===`optional`?`:${e.name}?`:`:${e.name}`;return e.flatMap(e=>e.kind===`static`?[e.parts[0].value]:e.kind===`param`?[n(e.parts[0])]:(e.parts.length&&(console.warn(`❗${i([`red`,`bold`],`WARN`)}: At the moment Solid Router does not support mixed path segments.`),console.warn(` ${i([`magenta`],e.orig)} segment in ${i([`blue`],t)} route won't match as expected.`),console.warn()),[e.parts.map(e=>e.type===`static`?e.value:n(e)).join(``)])).join(`/`)},Kn=()=>{let e=[`🎉 Well done! You just created a new Solid route.`,`🚀 Success! A fresh Solid route is ready to roll.`,`🌟 Nice work! Another Solid route added to your app.`,`🧩 All set! A new Solid route has been scaffolded.`,`🔧 Scaffold complete! Your new Solid route is in place.`,`✅ Built! Your Solid route is scaffolded and ready.`,`✨ Fantastic! Your new Solid route is good to go.`,`🎯 Nailed it! A brand new Solid route just landed.`,`💫 Awesome! Another Solid route joins the party.`,`⚡ Lightning fast! A new Solid route created successfully.`];return e[Math.floor(Math.random()*e.length)]},qn=`import type { ParentComponent } from "solid-js";
|
|
4600
5502
|
|
|
4601
5503
|
export const AppProvider: ParentComponent = (props) => {
|
|
4602
5504
|
return props.children;
|
|
4603
5505
|
};
|
|
4604
|
-
`,
|
|
5506
|
+
`,Jn=`import type { ParentComponent } from "solid-js";
|
|
4605
5507
|
import { type QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
|
4606
5508
|
|
|
4607
5509
|
import { getQueryClient } from "./query";
|
|
@@ -4613,7 +5515,7 @@ export const AppProvider: ParentComponent<{ client?: QueryClient }> = (props) =>
|
|
|
4613
5515
|
</QueryClientProvider>
|
|
4614
5516
|
);
|
|
4615
5517
|
};
|
|
4616
|
-
`,
|
|
5518
|
+
`,Yn=`import { lazy, type JSX } from "solid-js";
|
|
4617
5519
|
import { hydrate as hydrateOrig, render } from "solid-js/web";
|
|
4618
5520
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
4619
5521
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
@@ -4658,7 +5560,7 @@ export const mount = async (
|
|
|
4658
5560
|
}
|
|
4659
5561
|
|
|
4660
5562
|
export default clientRenderFactory();
|
|
4661
|
-
`,
|
|
5563
|
+
`,Xn=`{
|
|
4662
5564
|
path: "{{path}}",
|
|
4663
5565
|
{{#if component}}
|
|
4664
5566
|
component: {{component}}_component,
|
|
@@ -4668,7 +5570,7 @@ export default clientRenderFactory();
|
|
|
4668
5570
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
4669
5571
|
{{/if}}
|
|
4670
5572
|
}
|
|
4671
|
-
`,
|
|
5573
|
+
`,Zn=`import type { JSX } from "solid-js";
|
|
4672
5574
|
|
|
4673
5575
|
import {
|
|
4674
5576
|
generateHydrationScript,
|
|
@@ -4723,11 +5625,44 @@ export const renderWrapper: SSRRenderWrapper = (context, render) => {
|
|
|
4723
5625
|
export const renderToString: RenderToStringWrapper<
|
|
4724
5626
|
() => RouterFactoryReturn<JSX.Element>,
|
|
4725
5627
|
Parameters<typeof renderToStringAsync>[1]
|
|
4726
|
-
> = async (resolver, { headerTags = [], ...options } = {}) => {
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
5628
|
+
> = async (resolver, { headerTags = [], timeoutMs = 30_000, ...options } = {}) => {
|
|
5629
|
+
/**
|
|
5630
|
+
* renderToStringAsync adaptation.
|
|
5631
|
+
* Solid arms its internal timeout before invoking the component,
|
|
5632
|
+
* so a component throwing synchronously leaves that timeout promise orphaned -
|
|
5633
|
+
* its rejection, 30s later, is unhandled and fatal to the process.
|
|
5634
|
+
* renderToStream's thenable resolves with the same html;
|
|
5635
|
+
* deferring the call turns a sync throw into an immediate, catchable rejection carrying the real error,
|
|
5636
|
+
* and the replacement timeout below is pre-observed, so it can never reject unhandled.
|
|
5637
|
+
* */
|
|
5638
|
+
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
5639
|
+
|
|
5640
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
5641
|
+
timeoutHandle = setTimeout(() => {
|
|
5642
|
+
reject(new Error("SSR: solid render timed out"));
|
|
5643
|
+
}, timeoutMs);
|
|
5644
|
+
});
|
|
5645
|
+
|
|
5646
|
+
timeout.catch(() => {});
|
|
5647
|
+
|
|
5648
|
+
try {
|
|
5649
|
+
return {
|
|
5650
|
+
head: [...headerTags, generateHydrationScript()].join("\\n"),
|
|
5651
|
+
html: await Promise.race([
|
|
5652
|
+
Promise.resolve().then(() => {
|
|
5653
|
+
// typed as a stream handle, but resolving to the full html at runtime.
|
|
5654
|
+
// renderToStringAsync itself races it the same way.
|
|
5655
|
+
return renderToStreamOrig(
|
|
5656
|
+
() => resolver().component,
|
|
5657
|
+
options,
|
|
5658
|
+
) as unknown as Promise<string>;
|
|
5659
|
+
}),
|
|
5660
|
+
timeout,
|
|
5661
|
+
]),
|
|
5662
|
+
};
|
|
5663
|
+
} finally {
|
|
5664
|
+
clearTimeout(timeoutHandle);
|
|
5665
|
+
}
|
|
4731
5666
|
};
|
|
4732
5667
|
|
|
4733
5668
|
export const renderToStream: RenderToStreamWrapper<
|
|
@@ -4749,7 +5684,12 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
4749
5684
|
};
|
|
4750
5685
|
|
|
4751
5686
|
export default serverRenderFactory<true>();
|
|
4752
|
-
`,
|
|
5687
|
+
`,Qn=`declare module "virtual:kosmo/tsq-client" {
|
|
5688
|
+
import type { QueryClient, QueryClientConfig } from "@tanstack/solid-query";
|
|
5689
|
+
export const createQueryClient: (options?: QueryClientConfig) => QueryClient;
|
|
5690
|
+
export const getQueryClient: () => QueryClient;
|
|
5691
|
+
}
|
|
5692
|
+
`,$n=`/* @jsxImportSource solid-js */
|
|
4753
5693
|
|
|
4754
5694
|
import styles from "./styles.module.css";
|
|
4755
5695
|
|
|
@@ -4790,7 +5730,7 @@ export default function PageSample(props: {
|
|
|
4790
5730
|
</div>
|
|
4791
5731
|
);
|
|
4792
5732
|
}
|
|
4793
|
-
`,
|
|
5733
|
+
`,er=`/* @jsxImportSource solid-js */
|
|
4794
5734
|
|
|
4795
5735
|
import styles from "./styles.module.css";
|
|
4796
5736
|
|
|
@@ -4842,7 +5782,7 @@ export default function PageSample(props: {
|
|
|
4842
5782
|
</div>
|
|
4843
5783
|
);
|
|
4844
5784
|
}
|
|
4845
|
-
`,
|
|
5785
|
+
`,tr=`* {
|
|
4846
5786
|
margin: 0;
|
|
4847
5787
|
padding: 0;
|
|
4848
5788
|
box-sizing: border-box;
|
|
@@ -4977,7 +5917,7 @@ export default function PageSample(props: {
|
|
|
4977
5917
|
align-items: center;
|
|
4978
5918
|
gap: 0.25rem;
|
|
4979
5919
|
}
|
|
4980
|
-
`,
|
|
5920
|
+
`,nr=`/* @jsxImportSource solid-js */
|
|
4981
5921
|
|
|
4982
5922
|
import styles from "./styles.module.css";
|
|
4983
5923
|
|
|
@@ -5043,26 +5983,27 @@ export default function WelcomePage() {
|
|
|
5043
5983
|
</div>
|
|
5044
5984
|
);
|
|
5045
5985
|
}
|
|
5046
|
-
`,
|
|
5986
|
+
`,rr=`export * from "virtual:kosmo/tsq-client";
|
|
5987
|
+
`,ir=`import { QueryClient } from "@tanstack/solid-query";
|
|
5047
5988
|
|
|
5048
|
-
let client
|
|
5989
|
+
let client = undefined;
|
|
5049
5990
|
|
|
5050
|
-
export const createQueryClient = (options
|
|
5991
|
+
export const createQueryClient = (options) => {
|
|
5051
5992
|
client = new QueryClient(options);
|
|
5052
5993
|
return client;
|
|
5053
5994
|
};
|
|
5054
5995
|
|
|
5055
|
-
export const getQueryClient = ()
|
|
5996
|
+
export const getQueryClient = () => {
|
|
5056
5997
|
if (!client) {
|
|
5057
5998
|
client = new QueryClient();
|
|
5058
5999
|
}
|
|
5059
6000
|
return client;
|
|
5060
6001
|
};
|
|
5061
|
-
`,
|
|
6002
|
+
`,ar=`import { QueryClient } from "@tanstack/solid-query";
|
|
5062
6003
|
|
|
5063
|
-
import { store } from "{{ createImport '
|
|
6004
|
+
import { store } from "{{ createImport 'libCore' 'ssr' }}";
|
|
5064
6005
|
|
|
5065
|
-
export const createQueryClient = (options
|
|
6006
|
+
export const createQueryClient = (options) => {
|
|
5066
6007
|
const client = new QueryClient(options);
|
|
5067
6008
|
const ctx = store?.getStore();
|
|
5068
6009
|
if (ctx) {
|
|
@@ -5071,7 +6012,7 @@ export const createQueryClient = (options?: QueryClientConfig): QueryClient => {
|
|
|
5071
6012
|
return client;
|
|
5072
6013
|
};
|
|
5073
6014
|
|
|
5074
|
-
export const getQueryClient = ()
|
|
6015
|
+
export const getQueryClient = () => {
|
|
5075
6016
|
const ctx = store?.getStore();
|
|
5076
6017
|
if (!ctx) {
|
|
5077
6018
|
throw new Error("getQueryClient(): called outside an SSR request scope");
|
|
@@ -5079,9 +6020,9 @@ export const getQueryClient = (): QueryClient => {
|
|
|
5079
6020
|
if (!ctx.tsqClient) {
|
|
5080
6021
|
ctx.tsqClient = new QueryClient();
|
|
5081
6022
|
}
|
|
5082
|
-
return ctx.tsqClient
|
|
6023
|
+
return ctx.tsqClient;
|
|
5083
6024
|
};
|
|
5084
|
-
`,
|
|
6025
|
+
`,or=`import type { JSX, ParentComponent } from "solid-js";
|
|
5085
6026
|
import { Router, type RouteDefinition } from "@solidjs/router";
|
|
5086
6027
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
5087
6028
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
@@ -5117,7 +6058,7 @@ export const createRouters = (
|
|
|
5117
6058
|
}
|
|
5118
6059
|
|
|
5119
6060
|
export default createRouterFactory<RouteDefinition, JSX.Element>();
|
|
5120
|
-
`,
|
|
6061
|
+
`,sr=`export type ComponentLoader = () => Promise<{
|
|
5121
6062
|
preload?: () => Promise<unknown>;
|
|
5122
6063
|
}>;
|
|
5123
6064
|
|
|
@@ -5132,10 +6073,10 @@ export const loaderFactory = (opt?: { withPreload?: boolean }) => {
|
|
|
5132
6073
|
return opt?.withPreload ? { preload } : {};
|
|
5133
6074
|
};
|
|
5134
6075
|
};
|
|
5135
|
-
`,
|
|
6076
|
+
`,cr=`export type MaybeWrapped<T> = import("solid-js/store").Store<T> | T;
|
|
5136
6077
|
|
|
5137
6078
|
export { unwrap } from "solid-js/store";
|
|
5138
|
-
`,
|
|
6079
|
+
`,lr=`import type { ParentComponent } from "solid-js";
|
|
5139
6080
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
5140
6081
|
|
|
5141
6082
|
const App: ParentComponent = (props) => {
|
|
@@ -5143,7 +6084,7 @@ const App: ParentComponent = (props) => {
|
|
|
5143
6084
|
};
|
|
5144
6085
|
|
|
5145
6086
|
export default App;
|
|
5146
|
-
`,
|
|
6087
|
+
`,ur=`import { A, type AnchorProps } from "@solidjs/router";
|
|
5147
6088
|
import { type JSXElement, splitProps } from "solid-js";
|
|
5148
6089
|
|
|
5149
6090
|
import { pageRouteMap, type LinkProps } from "{{ createImport 'libCore' }}";
|
|
@@ -5168,7 +6109,7 @@ export default function Link(
|
|
|
5168
6109
|
|
|
5169
6110
|
return <A {...{ ...restProps, href: href() }}>{knownProps.children}</A>;
|
|
5170
6111
|
}
|
|
5171
|
-
`,
|
|
6112
|
+
`,dr=`import renderFactory, {
|
|
5172
6113
|
createRoutes,
|
|
5173
6114
|
hydrate,
|
|
5174
6115
|
mount,
|
|
@@ -5195,7 +6136,7 @@ if (root) {
|
|
|
5195
6136
|
} else {
|
|
5196
6137
|
console.error("❌ Root element not found!");
|
|
5197
6138
|
}
|
|
5198
|
-
`,
|
|
6139
|
+
`,fr=`import renderFactory, {
|
|
5199
6140
|
createRoutes,
|
|
5200
6141
|
renderToStream,
|
|
5201
6142
|
renderToString,
|
|
@@ -5222,31 +6163,19 @@ export default renderFactory(() => {
|
|
|
5222
6163
|
},
|
|
5223
6164
|
};
|
|
5224
6165
|
});
|
|
5225
|
-
`,
|
|
5226
|
-
<html lang="en">
|
|
5227
|
-
<head>
|
|
5228
|
-
<meta charset="UTF-8" />
|
|
5229
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5230
|
-
<!--app-head-->
|
|
5231
|
-
</head>
|
|
5232
|
-
<body>
|
|
5233
|
-
<div id="app"><!--app-html--></div>
|
|
5234
|
-
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
5235
|
-
</body>
|
|
5236
|
-
</html>
|
|
5237
|
-
`,Wn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
6166
|
+
`,pr=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
5238
6167
|
|
|
5239
6168
|
export default function Page() {
|
|
5240
6169
|
return <PageSample />;
|
|
5241
6170
|
}
|
|
5242
|
-
`,
|
|
6171
|
+
`,mr=`import type { ParentComponent } from "solid-js";
|
|
5243
6172
|
|
|
5244
6173
|
const Layout: ParentComponent = (props) => {
|
|
5245
6174
|
return props.children;
|
|
5246
6175
|
};
|
|
5247
6176
|
|
|
5248
6177
|
export default Layout;
|
|
5249
|
-
`,
|
|
6178
|
+
`,hr=`import PageSample from "{{ createImport 'lib' 'pageSamples/page.tsx' }}";
|
|
5250
6179
|
|
|
5251
6180
|
export default function Page() {
|
|
5252
6181
|
return PageSample({
|
|
@@ -5259,8 +6188,8 @@ export default function Page() {
|
|
|
5259
6188
|
},
|
|
5260
6189
|
});
|
|
5261
6190
|
}
|
|
5262
|
-
`,
|
|
5263
|
-
`,
|
|
6191
|
+
`,gr=`export { default } from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}";
|
|
6192
|
+
`,_r=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
5264
6193
|
|
|
5265
6194
|
import app from "./app";
|
|
5266
6195
|
|
|
@@ -5275,286 +6204,130 @@ export default routerFactory((routes) => {
|
|
|
5275
6204
|
},
|
|
5276
6205
|
};
|
|
5277
6206
|
});
|
|
5278
|
-
`,
|
|
5279
|
-
export { default as apiApp } from "{{ createImport 'api' 'app' }}";
|
|
5280
|
-
{{else}}
|
|
5281
|
-
export const apiApp = undefined;
|
|
5282
|
-
{{/if}}
|
|
5283
|
-
`,tr=`import { AsyncLocalStorage } from "node:async_hooks";
|
|
5284
|
-
|
|
5285
|
-
import type { FetchApp, NodeApp } from "@kosmojs/core";
|
|
5286
|
-
|
|
5287
|
-
export type RequestContext = {
|
|
5288
|
-
headers?: HeadersInit;
|
|
5289
|
-
tsqClient?: unknown;
|
|
5290
|
-
error?: unknown;
|
|
5291
|
-
};
|
|
5292
|
-
|
|
5293
|
-
export const redirectCodes = [
|
|
5294
|
-
// Moved Permanently
|
|
5295
|
-
301,
|
|
5296
|
-
// Found (temporary)
|
|
5297
|
-
302,
|
|
5298
|
-
// See Other (redirect after POST)
|
|
5299
|
-
303,
|
|
5300
|
-
// Temporary Redirect (preserves method)
|
|
5301
|
-
307,
|
|
5302
|
-
// Permanent Redirect (preserves method)
|
|
5303
|
-
308,
|
|
5304
|
-
];
|
|
5305
|
-
|
|
5306
|
-
/**
|
|
5307
|
-
* Origin used to absolutize the relative URLs the client produces.
|
|
5308
|
-
* Never resolved over the network; the host part is irrelevant to
|
|
5309
|
-
* route matching in both Hono and Koa.
|
|
5310
|
-
* */
|
|
5311
|
-
export const ssrOrigin = "http://ssr.local";
|
|
5312
|
-
|
|
5313
|
-
/**
|
|
5314
|
-
* Maximum redirect hops, mirroring the fetch spec limit.
|
|
5315
|
-
* */
|
|
5316
|
-
export const maxRedirects = 5;
|
|
5317
|
-
|
|
5318
|
-
/**
|
|
5319
|
-
* Request-scoped context store.
|
|
5320
|
-
* Server-only module - never reaches browser bundles.
|
|
5321
|
-
* */
|
|
5322
|
-
export const store = new AsyncLocalStorage<RequestContext>();
|
|
5323
|
-
|
|
5324
|
-
export const isFetchApp = (app: FetchApp | NodeApp): app is FetchApp => {
|
|
5325
|
-
return typeof (app as FetchApp).fetch === "function";
|
|
5326
|
-
};
|
|
5327
|
-
`,nr=`import { type RequestContext, store } from "./base";
|
|
5328
|
-
|
|
5329
|
-
import { renderWrapper } from "{{ createImport 'libEntry' 'server' }}";
|
|
5330
|
-
|
|
5331
|
-
export { default as ssrApp } from "{{ createImport 'entry' 'server' }}";
|
|
5332
|
-
export { apiApp } from "{{ createImport 'lib' '@ssr/api' }}";
|
|
6207
|
+
`,vr=v((e,t)=>{let{generators:n=[]}=e.config,{createPath:r,createImportHelpers:i}=S(e),{render:a,renderToFile:o}=w({helpers:{...i({origin:`lib`}),...A()},partials:{routePartial:Xn}}),{renderToFile:s}=w({helpers:i({origin:`src`})}),c=Gn(),d=e=>!e?.trim().length,f=l(t?.templates,hr),p=async e=>{for(let{kind:t,entry:n}of e)t===`pageRoute`?await s(r.pages(n.file),n.name===`index`?gr:f(n.name,n),{route:n,message:Kn()},{overwrite:d}):t===`pageLayout`&&await s(r.pages(n.file),mr,{route:n},{overwrite:d})},m=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(E),n=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),i=c(b(n));for(let[e,t]of[[`client.ts`,Yn],[`server.ts`,Zn]])await o(r.libEntry(e),t,{pageEntries:n,nestedRoutes:i});await o(r.lib(`router.tsx`),or,{entries:e,indexRoutes:t})};return{config({command:e}){let{templates:r,...i}={...t};return{oxc:{jsx:{importSource:`solid-js`}},plugins:e===`build`?[N({...i,...n.some(e=>e.meta.slot===`ssr`)?{ssr:!0,solid:{...i?.solid,hydratable:!0}}:{}})]:[N({...i,dev:!0,hot:!0})]}},async start(){for(let[e,n]of[[`env.d.ts`,Qn],[`solid.ts`,sr],[`unwrap.ts`,cr],[`pageSamples/styles.module.css`,tr],[`pageSamples/welcome.tsx`,nr],[`pageSamples/page.tsx`,er],[`pageSamples/404.tsx`,$n],...t?.tanstack?.query?[[`app.tsx`,Jn],[`query.ts`,rr]]:[[`app.tsx`,qn],[`query.ts`,`/** tanstack query disabled */`]]])await o(r.lib(e),n,{});for(let[e,t]of[[`pages/404.tsx`,pr],[`components/Link.tsx`,ur],[`app.tsx`,lr],[`router.ts`,_r]])await s(r.src(e),t,{entryDir:u.entryDir},{overwrite:d});for(let[e,t]of[[`client.ts`,dr],[`server.ts`,fr]])await s(r.entry(e),t,{},{overwrite:d})},async watch(e,t){await p(e.filter(g(t,[`create`]))),await m(e)},async build(e){await p(e),await m(e)},virtualModules(){return t?.tanstack?.query?[{specifier:`virtual:kosmo/tsq-client`,csr:a(ir,{}),ssr:a(ar,{})}]:[]}}}),yr=_({meta:{name:`SolidJS`,slot:`frontend`,jsx:`preserve`,jsxImportSource:`solid-js`},dependencies(e){return{"solid-js":G.devDependencies[`solid-js`],"@solidjs/router":G.devDependencies[`@solidjs/router`],...e?.tanstack?.query?{"@tanstack/solid-query":G.devDependencies[`@tanstack/solid-query`]}:{}}},factory:vr}),br=`import { join } from "node:path";
|
|
5333
6208
|
|
|
5334
|
-
|
|
5335
|
-
* Wraps a render call, making the given context visible to every
|
|
5336
|
-
* fetch dispatch that happens during it - across await points,
|
|
5337
|
-
* stream chunks and parallel component data loads.
|
|
5338
|
-
* */
|
|
5339
|
-
export const withSsrContext = <T>(
|
|
5340
|
-
context: RequestContext,
|
|
5341
|
-
render: () => T,
|
|
5342
|
-
): T => {
|
|
5343
|
-
return store.run(context, () => renderWrapper(context, render));
|
|
5344
|
-
};
|
|
6209
|
+
import { compile } from "path-to-regexp";
|
|
5345
6210
|
|
|
5346
|
-
|
|
5347
|
-
return store.getStore()?.error;
|
|
5348
|
-
};
|
|
5349
|
-
`,rr=`import type { FetchApp, NodeApp } from "@kosmojs/core";
|
|
5350
|
-
import type { Transport } from "@kosmojs/core/fetch";
|
|
6211
|
+
import type { PageRoute } from "@kosmojs/core";
|
|
5351
6212
|
|
|
5352
|
-
import {
|
|
5353
|
-
|
|
5354
|
-
maxRedirects,
|
|
5355
|
-
redirectCodes,
|
|
5356
|
-
ssrOrigin,
|
|
5357
|
-
store,
|
|
5358
|
-
} from "./base";
|
|
6213
|
+
import routes from "{{ createImport 'lib' 'ssg:routes' }}";
|
|
6214
|
+
import { base } from "{{ createImport 'libCore' }}";
|
|
5359
6215
|
|
|
5360
|
-
|
|
6216
|
+
type StaticParams = Array<Array<string | number | Array<string | number>>>;
|
|
5361
6217
|
|
|
5362
6218
|
/**
|
|
5363
|
-
*
|
|
6219
|
+
* Where a page declares the parameter sets to pre-render:
|
|
6220
|
+
* - a \`staticParams\` named export - React/Solid page modules,
|
|
6221
|
+
* a plain \`<script>\` block in Vue, a \`<script module>\` block in Svelte
|
|
6222
|
+
* - \`staticParams\` in MDX frontmatter
|
|
6223
|
+
* Each entry is positional, in the route's parameter order.
|
|
5364
6224
|
* */
|
|
5365
|
-
const
|
|
5366
|
-
|
|
6225
|
+
const staticParamsOf = (module: unknown): StaticParams | undefined => {
|
|
6226
|
+
const { staticParams, frontmatter } = (module ?? {}) as {
|
|
6227
|
+
staticParams?: unknown;
|
|
6228
|
+
frontmatter?: { staticParams?: unknown };
|
|
6229
|
+
};
|
|
6230
|
+
const value = staticParams ?? frontmatter?.staticParams;
|
|
6231
|
+
return Array.isArray(value) ? (value as StaticParams) : undefined;
|
|
5367
6232
|
};
|
|
5368
6233
|
|
|
5369
|
-
const
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
* */
|
|
5379
|
-
const url = new URL(request.url);
|
|
5380
|
-
|
|
5381
|
-
const payload = ["GET", "HEAD"].includes(request.method)
|
|
5382
|
-
? undefined
|
|
5383
|
-
: Buffer.from(await request.arrayBuffer());
|
|
5384
|
-
|
|
5385
|
-
const result = await inject(app.callback() as never, {
|
|
5386
|
-
method: request.method as never,
|
|
5387
|
-
url: url.pathname + url.search,
|
|
5388
|
-
headers: Object.fromEntries(request.headers),
|
|
5389
|
-
...(payload?.length ? { payload } : {}),
|
|
5390
|
-
});
|
|
5391
|
-
|
|
5392
|
-
const headers = new Headers();
|
|
5393
|
-
|
|
5394
|
-
for (const [key, value] of Object.entries(result.headers)) {
|
|
5395
|
-
for (const entry of Array.isArray(value) ? value : [value]) {
|
|
5396
|
-
if (entry !== undefined) {
|
|
5397
|
-
headers.append(key, String(entry));
|
|
5398
|
-
}
|
|
5399
|
-
}
|
|
6234
|
+
const paramsMapper = (
|
|
6235
|
+
params: PageRoute["params"],
|
|
6236
|
+
value: StaticParams[number],
|
|
6237
|
+
) => {
|
|
6238
|
+
return params.schema.reduce<Record<string, unknown>>(
|
|
6239
|
+
(map, { name, kind }, i) => {
|
|
6240
|
+
if (kind === "splat") {
|
|
6241
|
+
if (Array.isArray(value[i]) && value[i].length) {
|
|
6242
|
+
map[name] = value[i].map(String);
|
|
5400
6243
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
* 204/304 responses must not carry a body per the Response
|
|
5404
|
-
* constructor contract.
|
|
5405
|
-
* */
|
|
5406
|
-
const body = [204, 304].includes(result.statusCode)
|
|
5407
|
-
? null
|
|
5408
|
-
: new Uint8Array(result.rawPayload);
|
|
5409
|
-
|
|
5410
|
-
return new Response(body, {
|
|
5411
|
-
status: result.statusCode,
|
|
5412
|
-
statusText: result.statusMessage,
|
|
5413
|
-
headers,
|
|
5414
|
-
});
|
|
5415
|
-
};
|
|
5416
|
-
};
|
|
5417
|
-
|
|
5418
|
-
const createTransport = (app: FetchApp | NodeApp): Transport => {
|
|
5419
|
-
const dispatch = createDispatch(app);
|
|
5420
|
-
|
|
5421
|
-
/**
|
|
5422
|
-
* Build a fetch-compatible transport that dispatches requests
|
|
5423
|
-
* directly into the given app - no sockets, no interception.
|
|
5424
|
-
* Redirects are followed in-process, including the 303 and 301/302 method rewrite to GET.
|
|
5425
|
-
* */
|
|
5426
|
-
return async (input, init) => {
|
|
5427
|
-
/**
|
|
5428
|
-
* Request-scoped headers act as defaults: anything set explicitly
|
|
5429
|
-
* on the call itself wins over forwarded values.
|
|
5430
|
-
* */
|
|
5431
|
-
const headers = new Headers(init?.headers);
|
|
5432
|
-
|
|
5433
|
-
// When the body is FormData, the Request constructor sets a multipart
|
|
5434
|
-
// Content-Type with a fresh boundary. A forwarded Content-Type default would
|
|
5435
|
-
// override that boundary and desync it from the serialized body, so never
|
|
5436
|
-
// forward Content-Type for FormData bodies.
|
|
5437
|
-
const isFormBody = init?.body instanceof FormData;
|
|
5438
|
-
|
|
5439
|
-
for (const [key, value] of new Headers(headersProvider() || undefined)) {
|
|
5440
|
-
if (isFormBody && key.toLowerCase() === "content-type") {
|
|
5441
|
-
continue;
|
|
5442
|
-
}
|
|
5443
|
-
if (!headers.has(key)) {
|
|
5444
|
-
headers.set(key, value);
|
|
5445
|
-
}
|
|
5446
|
-
}
|
|
5447
|
-
|
|
5448
|
-
let request = new Request(new URL(String(input), ssrOrigin), {
|
|
5449
|
-
...init,
|
|
5450
|
-
headers,
|
|
5451
|
-
});
|
|
5452
|
-
|
|
5453
|
-
/**
|
|
5454
|
-
* Bodies are buffered once so they can be replayed across
|
|
5455
|
-
* 307/308 hops; the client only ever sends strings, FormData
|
|
5456
|
-
* and buffer-ish payloads, so this is safe and cheap.
|
|
5457
|
-
* */
|
|
5458
|
-
const body = ["GET", "HEAD"].includes(request.method)
|
|
5459
|
-
? undefined
|
|
5460
|
-
: await request.arrayBuffer();
|
|
5461
|
-
|
|
5462
|
-
for (let hop = 0; ; hop++) {
|
|
5463
|
-
if (hop === maxRedirects) {
|
|
5464
|
-
throw new TypeError("Failed to fetch: too many redirects");
|
|
6244
|
+
} else if (value[i] !== undefined) {
|
|
6245
|
+
map[name] = String(value[i]);
|
|
5465
6246
|
}
|
|
6247
|
+
return map;
|
|
6248
|
+
},
|
|
6249
|
+
{},
|
|
6250
|
+
);
|
|
6251
|
+
};
|
|
5466
6252
|
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
);
|
|
5472
|
-
|
|
5473
|
-
const location = response.headers.get("location");
|
|
5474
|
-
|
|
5475
|
-
if (!location || !redirectCodes.includes(response.status)) {
|
|
5476
|
-
return response;
|
|
5477
|
-
}
|
|
6253
|
+
export default Object.entries(routes)
|
|
6254
|
+
.flatMap(([name, { module, pathPattern, params }]) => {
|
|
6255
|
+
if (!params.schema.length) {
|
|
6256
|
+
// static route
|
|
6257
|
+
return [pathPattern.replace(/^index\\/?/, "")];
|
|
6258
|
+
}
|
|
5478
6259
|
|
|
5479
|
-
|
|
5480
|
-
response.status === 303 ||
|
|
5481
|
-
([301, 302].includes(response.status) && request.method === "POST")
|
|
5482
|
-
? "GET"
|
|
5483
|
-
: request.method;
|
|
6260
|
+
const staticParams = staticParamsOf(module);
|
|
5484
6261
|
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
});
|
|
6262
|
+
// a dynamic route without staticParams has nothing to pre-render
|
|
6263
|
+
if (!staticParams) {
|
|
6264
|
+
return [];
|
|
5489
6265
|
}
|
|
5490
|
-
};
|
|
5491
|
-
};
|
|
5492
6266
|
|
|
5493
|
-
const
|
|
6267
|
+
const toPath = compile(pathPattern);
|
|
5494
6268
|
|
|
5495
|
-
|
|
5496
|
-
? async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
6269
|
+
return staticParams.flatMap((entry) => {
|
|
5497
6270
|
try {
|
|
5498
|
-
|
|
5499
|
-
if (response?.ok) {
|
|
5500
|
-
return response;
|
|
5501
|
-
}
|
|
5502
|
-
// the rethrow here needed cause ssrTransport does not throw on non-2xx responses
|
|
5503
|
-
throw new SSRFetchError([
|
|
5504
|
-
input,
|
|
5505
|
-
response,
|
|
5506
|
-
typeof response?.text === "function"
|
|
5507
|
-
? await response.text()
|
|
5508
|
-
: response?.statusText,
|
|
5509
|
-
]);
|
|
6271
|
+
return [toPath(paramsMapper(params, entry) as never)];
|
|
5510
6272
|
} catch (error) {
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
* Storing the error here keeps it observable regardless of how the framework handles the loader rejection.
|
|
5515
|
-
* */
|
|
5516
|
-
const storage = store.getStore();
|
|
5517
|
-
if (storage) {
|
|
5518
|
-
storage.error = error;
|
|
5519
|
-
}
|
|
5520
|
-
throw error;
|
|
6273
|
+
console.error(\`❗SSG: Failed building path for \${name}\`);
|
|
6274
|
+
console.error(error);
|
|
6275
|
+
return [];
|
|
5521
6276
|
}
|
|
5522
|
-
}
|
|
5523
|
-
|
|
6277
|
+
});
|
|
6278
|
+
})
|
|
6279
|
+
.map((path) => join(base, path));
|
|
6280
|
+
`,xr=`import type { PageRoute } from "@kosmojs/core";
|
|
5524
6281
|
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
response: Response,
|
|
5529
|
-
message: string | undefined,
|
|
5530
|
-
]) {
|
|
5531
|
-
const pathname = pathnameOf(input);
|
|
5532
|
-
const status = response.status ?? "unknown";
|
|
5533
|
-
super(\`\${pathname}: \${status} [ \${message} ]\`.trim());
|
|
5534
|
-
this.name = "SSRFetchError";
|
|
5535
|
-
}
|
|
5536
|
-
}
|
|
6282
|
+
{{#each pageRoutes}}
|
|
6283
|
+
import * as {{id}} from "{{ createImport 'pages' file }}";
|
|
6284
|
+
{{/each}}
|
|
5537
6285
|
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
}
|
|
5549
|
-
|
|
5550
|
-
|
|
6286
|
+
type SSGRoute = {
|
|
6287
|
+
// the page module as imported; the shape differs per framework
|
|
6288
|
+
module: unknown;
|
|
6289
|
+
pathPattern: string;
|
|
6290
|
+
params: PageRoute["params"];
|
|
6291
|
+
};
|
|
6292
|
+
|
|
6293
|
+
const routeMap: Record<string, SSGRoute> = {
|
|
6294
|
+
{{#each pageRoutes}}
|
|
6295
|
+
"{{name}}": {
|
|
6296
|
+
module: {{id}},
|
|
6297
|
+
pathPattern: "{{pathPattern}}",
|
|
6298
|
+
params: {{serializeParams .}},
|
|
6299
|
+
},
|
|
6300
|
+
{{/each}}
|
|
6301
|
+
};
|
|
6302
|
+
|
|
6303
|
+
export default routeMap;
|
|
6304
|
+
`,Sr=v(a=>{let{generators:o=[],refineTypeName:s,...c}={...a.config},{base:l}=c,{createPath:u,createImportHelpers:f}=S(a),{renderToFile:p}=w({helpers:{...f({origin:`lib`}),...A(),serializeParams(e){return JSON.stringify(e.params)}}}),m=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(E);await p(u.lib(`ssg:routes.ts`),xr,{pageRoutes:t})};return{async start(){await p(u.lib(`ssg.ts`),br,{})},async watch(e){await m(e)},async build(e){await m(e)},async postBuild(){let s=u.distDir(`ssg`),f=r(s,`../ssr/server.js`);if(!await ce(f,le.F_OK).then(()=>!0,()=>!1)){console.error(),console.error(i(`red`,`❗Please enable ssrGenerator in ${a.name}/kosmo.config.ts`)),console.error(` SSG generator can not run without SSR server`),console.error();return}let p=ee(`${a.name}: SSG`);p.append(`preparing...`);let{createApp:m}=await import(f);p.append(`bundling routes...`),await M(y(c,...o.map(({factory:e})=>e(a).config?.({kind:`client`,command:`build`})),{root:u.lib(),appType:`custom`,plugins:[O.tsconfigPaths(a),O.nodePrefix(),O.virtualModules(d(a,o),{kind:`csr`,command:`build`})],resolve:{conditions:[`node`]},build:{ssr:u.lib(`ssg.ts`),target:`esnext`,sourcemap:!1,emptyOutDir:!0,rolldownOptions:{output:{dir:s,entryFileNames:`routes.js`,format:`esm`}}}}));try{let i=await import(t(s,`routes.js`)).then(e=>e.default),a=new Map,o=await m(e=>{a.set(new URL(e.url).pathname,{error:e.message})});for(let[e,t]of i.entries()){p.append(`[ ${e+1} of ${i.length} ] ${t}`);try{let e=await Cr(o,t);a.has(t)||a.set(t,{html:e})}catch(e){a.has(t)||a.set(t,{error:String(e)})}}let c=[...a.entries()].flatMap(([e,t])=>`error`in t?[[e,t.error]]:[]);if(c.length)throw p.failed(`failed ❗`),Error([`SSG: failed rendering ${c.length} route(s):`,...c.map(([e,t])=>` ${e} - ${t}`)].join(`
|
|
6305
|
+
`));await P(r(s,`../ssr/assets`),t(s,`assets`),{recursive:!0});let u=r(s,`../ssr/public`);await x(u)&&await P(u,s,{recursive:!0});for(let[r,i]of a)if(`html`in i){let a=t(s,n.relative(l,r),`index.html`);await ue(e(a),{recursive:!0}),await de(a,i.html,`utf8`)}p.succeed(`done ✨`)}finally{await F(`${s}/routes.js`)}}}}),Cr=async(e,t)=>{let n=await e.fetch(new Request(`http://localhost${t}`));if(!n.ok)throw Error(`app responded with ${n.status}`);return n.text()},wr=_({meta:{name:`SSG`,slot:`ssg`},factory:Sr}),q={type:`module`,private:!0,name:`@kosmojs/ssr-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,vite:`^8.2.2`},devDependencies:{"@hono/node-server":`^2.1.1`,hono:`^4.13.3`,tinyglobby:`^0.2.17`}},Tr=`import { type RequestContext, store } from "{{ createImport 'libCore' 'ssr' }}";
|
|
6306
|
+
import { renderWrapper } from "{{ createImport 'libEntry' 'server' }}";
|
|
6307
|
+
|
|
6308
|
+
export { default as backendApp } from "virtual:kosmo/backend-app";
|
|
6309
|
+
|
|
6310
|
+
export { default as ssrApp } from "{{ createImport 'entry' 'server' }}";
|
|
6311
|
+
|
|
6312
|
+
/**
|
|
6313
|
+
* Wrap a render call, making the given context visible to every component
|
|
6314
|
+
* */
|
|
6315
|
+
export const withSsrContext = <T>(
|
|
6316
|
+
context: RequestContext,
|
|
6317
|
+
render: () => T,
|
|
6318
|
+
): T => {
|
|
6319
|
+
return store.run(context, () => renderWrapper(context, render));
|
|
6320
|
+
};
|
|
6321
|
+
|
|
6322
|
+
export const errorProvider = () => {
|
|
6323
|
+
return store.getStore()?.error;
|
|
5551
6324
|
};
|
|
5552
|
-
`,
|
|
6325
|
+
`,Er=`export const routeMap = [
|
|
5553
6326
|
{{#each pageRoutes}}
|
|
5554
6327
|
{ pathPattern: "{{honoPattern}}", renderMode: "{{renderMode}}" },
|
|
5555
6328
|
{{/each}}
|
|
5556
6329
|
];
|
|
5557
|
-
`,
|
|
6330
|
+
`,Dr=`import { access, chmod, constants, readFile, unlink } from "node:fs/promises";
|
|
5558
6331
|
import {
|
|
5559
6332
|
createServer,
|
|
5560
6333
|
type IncomingMessage,
|
|
@@ -5564,20 +6337,25 @@ import { extname, join, resolve } from "node:path";
|
|
|
5564
6337
|
import { fileURLToPath } from "node:url";
|
|
5565
6338
|
import { parseArgs, styleText } from "node:util";
|
|
5566
6339
|
|
|
5567
|
-
import {
|
|
6340
|
+
import { getRequestListener } from "@hono/node-server";
|
|
5568
6341
|
import { type Context, Hono } from "hono";
|
|
5569
6342
|
import { HTTPException } from "hono/http-exception";
|
|
5570
6343
|
import { stream } from "hono/streaming";
|
|
5571
6344
|
import { glob } from "tinyglobby";
|
|
5572
6345
|
|
|
5573
|
-
import
|
|
5574
|
-
|
|
5575
|
-
|
|
6346
|
+
import {
|
|
6347
|
+
type FetchApp,
|
|
6348
|
+
MIME_TYPES,
|
|
6349
|
+
type NodeApp,
|
|
6350
|
+
type SSRSetup,
|
|
6351
|
+
} from "@kosmojs/core";
|
|
5576
6352
|
|
|
5577
6353
|
import { routeMap } from "{{ createImport 'lib' '@ssr/routes' }}";
|
|
5578
6354
|
import { apiBase, base } from "{{ createImport 'libCore' }}";
|
|
6355
|
+
import { redirectCodes, ssrOrigin } from "{{ createImport 'libCore' 'ssr' }}";
|
|
5579
6356
|
|
|
5580
6357
|
const ROOT = import.meta.dirname;
|
|
6358
|
+
const HEAD_CLOSE_PATTERN = /<\\/head\\s*>/i;
|
|
5581
6359
|
|
|
5582
6360
|
type AssetInfo = {
|
|
5583
6361
|
file: string;
|
|
@@ -5587,9 +6365,13 @@ type AssetInfo = {
|
|
|
5587
6365
|
contentType: string;
|
|
5588
6366
|
// Cached size to set Content-Length without re-measuring the buffer.
|
|
5589
6367
|
size: number;
|
|
6368
|
+
// Cache-Control header - hashed assets are immutable, public/ files are not.
|
|
6369
|
+
cacheControl: string;
|
|
5590
6370
|
};
|
|
5591
6371
|
|
|
5592
|
-
export const createApp = async (
|
|
6372
|
+
export const createApp = async (
|
|
6373
|
+
errorHandler?: (error: Error & { url: string }) => void | undefined,
|
|
6374
|
+
) => {
|
|
5593
6375
|
// Import the SSR entry produced by Vite's ssr build.
|
|
5594
6376
|
const {
|
|
5595
6377
|
ssrApp,
|
|
@@ -5604,8 +6386,7 @@ export const createApp = async () => {
|
|
|
5604
6386
|
errorProvider: () => Error | undefined;
|
|
5605
6387
|
} = await import(\`\${ROOT}/app.js\`);
|
|
5606
6388
|
|
|
5607
|
-
// Read the client index.html that includes <!--app-
|
|
5608
|
-
// placeholders used for SSR injection.
|
|
6389
|
+
// Read the client index.html that includes <!--app-html--> placeholder
|
|
5609
6390
|
const template = await readFile(\`\${ROOT}/index.html\`, "utf8");
|
|
5610
6391
|
|
|
5611
6392
|
// Load the Vite manifest
|
|
@@ -5613,8 +6394,9 @@ export const createApp = async () => {
|
|
|
5613
6394
|
with: { type: "json" },
|
|
5614
6395
|
}).then((e) => e.default);
|
|
5615
6396
|
|
|
5616
|
-
const { renderToString, renderToStream } = ssrApp;
|
|
5617
|
-
|
|
6397
|
+
const { renderToString, renderToStream, onError } = ssrApp;
|
|
6398
|
+
|
|
6399
|
+
const [htmlStart, htmlEnd = ""] = template.split(/<!--\\s*app-html\\s*-->/);
|
|
5618
6400
|
|
|
5619
6401
|
const assets = await loadAssets(ROOT);
|
|
5620
6402
|
|
|
@@ -5660,6 +6442,29 @@ export const createApp = async () => {
|
|
|
5660
6442
|
};
|
|
5661
6443
|
};
|
|
5662
6444
|
|
|
6445
|
+
const handleError = (url: string, error: Error, fallback: Function) => {
|
|
6446
|
+
// assign, not spread: message and stack are non-enumerable on Error,
|
|
6447
|
+
// a spread silently drops them
|
|
6448
|
+
Object.assign(error, { url });
|
|
6449
|
+
if (onError) {
|
|
6450
|
+
onError(error as never);
|
|
6451
|
+
} else {
|
|
6452
|
+
fallback();
|
|
6453
|
+
}
|
|
6454
|
+
errorHandler?.(error as never);
|
|
6455
|
+
};
|
|
6456
|
+
|
|
6457
|
+
const injectHead = (html: string, head: string) => {
|
|
6458
|
+
const error = "WARN: missing </head> - required for SSR head injection";
|
|
6459
|
+
if (HEAD_CLOSE_PATTERN.test(html)) {
|
|
6460
|
+
return html.replace(HEAD_CLOSE_PATTERN, (headEnd) => {
|
|
6461
|
+
return [head, headEnd].join("\\n");
|
|
6462
|
+
});
|
|
6463
|
+
}
|
|
6464
|
+
console.error(error);
|
|
6465
|
+
return \`\${html}\\n<script>console.error("\${error}")<\/script>\`;
|
|
6466
|
+
};
|
|
6467
|
+
|
|
5663
6468
|
const renderPage = async (url: URL, ctx: Context) => {
|
|
5664
6469
|
const {
|
|
5665
6470
|
head = "",
|
|
@@ -5690,27 +6495,51 @@ export const createApp = async () => {
|
|
|
5690
6495
|
);
|
|
5691
6496
|
|
|
5692
6497
|
if (error) {
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
6498
|
+
const errorMessage = "WARN: SSR failed, fallback to CSR";
|
|
6499
|
+
handleError(ctx.req.url, error as never, () => {
|
|
6500
|
+
console.error(errorMessage);
|
|
6501
|
+
console.error(error);
|
|
6502
|
+
console.error();
|
|
6503
|
+
});
|
|
5696
6504
|
return [
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
\`<script>console.error("
|
|
6505
|
+
injectHead(
|
|
6506
|
+
htmlStart,
|
|
6507
|
+
\`<script>console.error("\${errorMessage}")<\/script>\`,
|
|
5700
6508
|
),
|
|
5701
6509
|
htmlEnd,
|
|
5702
6510
|
].join("");
|
|
5703
6511
|
}
|
|
5704
6512
|
|
|
5705
|
-
return [
|
|
5706
|
-
htmlStart.replace("<!--app-head-->", head),
|
|
5707
|
-
html ?? "",
|
|
5708
|
-
htmlEnd,
|
|
5709
|
-
].join("");
|
|
6513
|
+
return [injectHead(htmlStart, head), html ?? "", htmlEnd].join("");
|
|
5710
6514
|
};
|
|
5711
6515
|
|
|
5712
6516
|
const app = new Hono({ strict: false });
|
|
5713
6517
|
|
|
6518
|
+
// Static files win over routes, as they do in vite dev and behind a reverse proxy.
|
|
6519
|
+
// This covers hashed assets/ (JS, CSS, images, fonts, .map siblings) and public/ files.
|
|
6520
|
+
app.use(async (ctx, next) => {
|
|
6521
|
+
if (!["GET", "HEAD"].includes(ctx.req.method)) {
|
|
6522
|
+
return next();
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
const asset = assets.get(ctx.req.path);
|
|
6526
|
+
|
|
6527
|
+
if (!asset) {
|
|
6528
|
+
return next();
|
|
6529
|
+
}
|
|
6530
|
+
|
|
6531
|
+
return new Response(
|
|
6532
|
+
ctx.req.method === "HEAD" ? null : (asset.buffer as never),
|
|
6533
|
+
{
|
|
6534
|
+
headers: {
|
|
6535
|
+
"Content-Type": asset.contentType,
|
|
6536
|
+
"Content-Length": String(asset.size),
|
|
6537
|
+
"Cache-Control": asset.cacheControl,
|
|
6538
|
+
},
|
|
6539
|
+
},
|
|
6540
|
+
);
|
|
6541
|
+
});
|
|
6542
|
+
|
|
5714
6543
|
for (const { pathPattern, renderMode } of routeMap) {
|
|
5715
6544
|
app.get(join(base, pathPattern), async (ctx) => {
|
|
5716
6545
|
try {
|
|
@@ -5724,16 +6553,37 @@ export const createApp = async () => {
|
|
|
5724
6553
|
if (renderMode === "stream" && typeof renderToStream === "function") {
|
|
5725
6554
|
ctx.header("Content-Type", "text/html");
|
|
5726
6555
|
return stream(ctx, async (stream) => {
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
6556
|
+
let error: Error | undefined;
|
|
6557
|
+
|
|
6558
|
+
/**
|
|
6559
|
+
* Stream failures surface here, not in a catch upstream:
|
|
6560
|
+
* on solid and vue pipe rejects; react shell errors reject the render promise itself.
|
|
6561
|
+
* The shell may already be on the wire -
|
|
6562
|
+
* reporting is all that is left to do, the response cannot be replaced.
|
|
6563
|
+
* */
|
|
6564
|
+
try {
|
|
6565
|
+
const { head = "", html } = await withSsrContext(
|
|
6566
|
+
{
|
|
6567
|
+
headers: Object.fromEntries(ctx.req.raw.headers),
|
|
6568
|
+
url: ctx.req.url,
|
|
6569
|
+
},
|
|
6570
|
+
() => renderToStream(url, ssrOptions(), stream as never),
|
|
6571
|
+
);
|
|
6572
|
+
await stream.write(injectHead(htmlStart, head));
|
|
6573
|
+
await stream.pipe(html);
|
|
6574
|
+
error = errorProvider();
|
|
6575
|
+
await stream.write(htmlEnd);
|
|
6576
|
+
} catch (e: any) {
|
|
6577
|
+
error = e;
|
|
6578
|
+
}
|
|
6579
|
+
|
|
6580
|
+
if (error) {
|
|
6581
|
+
handleError(ctx.req.url, error, () => {
|
|
6582
|
+
console.error("ERROR: SSR stream render failed");
|
|
6583
|
+
console.error(error);
|
|
6584
|
+
console.error();
|
|
6585
|
+
});
|
|
6586
|
+
}
|
|
5737
6587
|
});
|
|
5738
6588
|
}
|
|
5739
6589
|
|
|
@@ -5758,21 +6608,6 @@ export const createApp = async () => {
|
|
|
5758
6608
|
}
|
|
5759
6609
|
|
|
5760
6610
|
app.get("/*", async (ctx) => {
|
|
5761
|
-
const { path } = ctx.req;
|
|
5762
|
-
|
|
5763
|
-
// If incoming request path matches something cached at startup, serve it directly.
|
|
5764
|
-
// This covers JS, CSS, images, fonts, etc., including their .map siblings.
|
|
5765
|
-
const asset = assets.get(path);
|
|
5766
|
-
|
|
5767
|
-
if (asset) {
|
|
5768
|
-
return new Response(asset.buffer as never, {
|
|
5769
|
-
headers: {
|
|
5770
|
-
"Content-Type": asset.contentType,
|
|
5771
|
-
"Content-Length": String(asset.size),
|
|
5772
|
-
},
|
|
5773
|
-
});
|
|
5774
|
-
}
|
|
5775
|
-
|
|
5776
6611
|
// render 404 page
|
|
5777
6612
|
if (typeof renderToString === "function") {
|
|
5778
6613
|
const url = new URL(ctx.req.url);
|
|
@@ -5791,47 +6626,47 @@ export const createApp = async () => {
|
|
|
5791
6626
|
* Build an in-memory asset graph, loading asset content into memory.
|
|
5792
6627
|
* The asset graph always includes every built asset URL so the SSR server
|
|
5793
6628
|
* can correctly recognize static asset requests.
|
|
6629
|
+
*
|
|
6630
|
+
* Two roots, each directory being its own allowlist - nothing else in the bundle root is served:
|
|
6631
|
+
* - assets/ - emitted by vite with content hashes, served at base/assets/, cacheable forever
|
|
6632
|
+
* - public/ - copied verbatim from the folder's public dir, served at base/, names are stable so clients must revalidate
|
|
5794
6633
|
* */
|
|
5795
|
-
const loadAssets = async (
|
|
5796
|
-
root: string,
|
|
5797
|
-
patterns: string | Array<string> = "**",
|
|
5798
|
-
) => {
|
|
5799
|
-
const mimeTypeMap: Record<string, string> = {
|
|
5800
|
-
".js": "application/javascript",
|
|
5801
|
-
".mjs": "application/javascript",
|
|
5802
|
-
".css": "text/css",
|
|
5803
|
-
".json": "application/json",
|
|
5804
|
-
".png": "image/png",
|
|
5805
|
-
".apng": "image/png",
|
|
5806
|
-
".jpg": "image/jpeg",
|
|
5807
|
-
".jpeg": "image/jpeg",
|
|
5808
|
-
".gif": "image/gif",
|
|
5809
|
-
".svg": "image/svg+xml",
|
|
5810
|
-
".ico": "image/x-icon",
|
|
5811
|
-
".woff": "font/woff",
|
|
5812
|
-
".woff2": "font/woff2",
|
|
5813
|
-
".ttf": "font/ttf",
|
|
5814
|
-
".webp": "image/webp",
|
|
5815
|
-
};
|
|
5816
|
-
|
|
6634
|
+
const loadAssets = async (root: string) => {
|
|
5817
6635
|
// Resolve HTTP Content-Type from the asset's file extension.
|
|
5818
6636
|
const contentTypeResolver = (filePath: string) => {
|
|
5819
6637
|
const ext = extname(filePath).toLowerCase();
|
|
5820
|
-
return
|
|
6638
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
5821
6639
|
};
|
|
5822
6640
|
|
|
5823
6641
|
// Map from URL path (as used in requests) to asset metadata.
|
|
5824
6642
|
const assetCache = new Map<string, AssetInfo>();
|
|
5825
6643
|
|
|
5826
|
-
const
|
|
5827
|
-
|
|
6644
|
+
const roots = [
|
|
6645
|
+
{
|
|
6646
|
+
folder: "assets",
|
|
6647
|
+
prefix: join(base, "assets"),
|
|
6648
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
6649
|
+
},
|
|
6650
|
+
{
|
|
6651
|
+
folder: "public",
|
|
6652
|
+
prefix: base,
|
|
6653
|
+
cacheControl: "no-cache",
|
|
6654
|
+
},
|
|
6655
|
+
];
|
|
6656
|
+
|
|
6657
|
+
for (const { folder, prefix, cacheControl } of roots) {
|
|
6658
|
+
const cwd = resolve(root, folder);
|
|
6659
|
+
|
|
6660
|
+
const readable = await access(cwd, constants.F_OK).then(
|
|
6661
|
+
() => true,
|
|
6662
|
+
() => false,
|
|
6663
|
+
);
|
|
6664
|
+
|
|
6665
|
+
if (!readable) {
|
|
6666
|
+
continue;
|
|
6667
|
+
}
|
|
5828
6668
|
|
|
5829
|
-
|
|
5830
|
-
await access(cwd, constants.R_OK)
|
|
5831
|
-
.then(() => true)
|
|
5832
|
-
.catch(() => false)
|
|
5833
|
-
) {
|
|
5834
|
-
const files = await glob(patterns, {
|
|
6669
|
+
const files = await glob("**", {
|
|
5835
6670
|
cwd,
|
|
5836
6671
|
onlyFiles: true,
|
|
5837
6672
|
absolute: false,
|
|
@@ -5839,11 +6674,12 @@ const loadAssets = async (
|
|
|
5839
6674
|
|
|
5840
6675
|
for (const file of files) {
|
|
5841
6676
|
const buffer = new Uint8Array(await readFile(resolve(cwd, file)));
|
|
5842
|
-
assetCache.set(join(
|
|
6677
|
+
assetCache.set(join(prefix, file), {
|
|
5843
6678
|
file,
|
|
5844
6679
|
buffer,
|
|
5845
6680
|
contentType: contentTypeResolver(file),
|
|
5846
|
-
size: buffer
|
|
6681
|
+
size: buffer.length,
|
|
6682
|
+
cacheControl,
|
|
5847
6683
|
});
|
|
5848
6684
|
}
|
|
5849
6685
|
}
|
|
@@ -5854,11 +6690,40 @@ const loadAssets = async (
|
|
|
5854
6690
|
type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;
|
|
5855
6691
|
|
|
5856
6692
|
const createNodeListener = (app: FetchApp | NodeApp): NodeListener => {
|
|
5857
|
-
return
|
|
6693
|
+
return typeof (app as FetchApp).fetch === "function"
|
|
5858
6694
|
? getRequestListener((app as FetchApp).fetch)
|
|
5859
6695
|
: (app as NodeApp).callback();
|
|
5860
6696
|
};
|
|
5861
6697
|
|
|
6698
|
+
/**
|
|
6699
|
+
* The folder's complete request surface as a single node:http listener:
|
|
6700
|
+
* API requests under \`apiBase\` go to the bundled backend, everything else to the SSR app.
|
|
6701
|
+
* \`startServer\` binds it to a port/socket; \`dist/run.js\` mounts it next to other folders.
|
|
6702
|
+
* */
|
|
6703
|
+
export const createListener = async (): Promise<NodeListener> => {
|
|
6704
|
+
const {
|
|
6705
|
+
backendApp,
|
|
6706
|
+
}: {
|
|
6707
|
+
backendApp: FetchApp | NodeApp;
|
|
6708
|
+
} = await import(\`\${ROOT}/app.js\`);
|
|
6709
|
+
|
|
6710
|
+
const ssrApp = await createApp();
|
|
6711
|
+
const apiPrefix = join(base, apiBase);
|
|
6712
|
+
|
|
6713
|
+
const ssrListener = createNodeListener(ssrApp as never);
|
|
6714
|
+
|
|
6715
|
+
const apiListener = backendApp
|
|
6716
|
+
? createNodeListener(backendApp as never)
|
|
6717
|
+
: async () => {};
|
|
6718
|
+
|
|
6719
|
+
return (req, res) => {
|
|
6720
|
+
const { pathname } = new URL(req.url ?? "/", ssrOrigin);
|
|
6721
|
+
return pathname === apiPrefix || pathname.startsWith(\`\${apiPrefix}/\`)
|
|
6722
|
+
? apiListener(req, res)
|
|
6723
|
+
: ssrListener(req, res);
|
|
6724
|
+
};
|
|
6725
|
+
};
|
|
6726
|
+
|
|
5862
6727
|
export const startServer = async ({
|
|
5863
6728
|
sock,
|
|
5864
6729
|
port,
|
|
@@ -5870,12 +6735,6 @@ export const startServer = async ({
|
|
|
5870
6735
|
throw new Error("Please provide either -p/--port or -s/--sock");
|
|
5871
6736
|
}
|
|
5872
6737
|
|
|
5873
|
-
const {
|
|
5874
|
-
apiApp,
|
|
5875
|
-
}: {
|
|
5876
|
-
apiApp: FetchApp | NodeApp;
|
|
5877
|
-
} = await import(\`\${ROOT}/app.js\`);
|
|
5878
|
-
|
|
5879
6738
|
if (sock) {
|
|
5880
6739
|
// Clean up any stale socket file before binding.
|
|
5881
6740
|
await unlink(sock).catch((error) => {
|
|
@@ -5892,23 +6751,7 @@ export const startServer = async ({
|
|
|
5892
6751
|
sock ? \`sock: \${sock}\` : \`port: \${port}\`,
|
|
5893
6752
|
);
|
|
5894
6753
|
|
|
5895
|
-
const
|
|
5896
|
-
const apiPrefix = join(base, apiBase);
|
|
5897
|
-
|
|
5898
|
-
const ssrListener = createNodeListener(ssrApp as never);
|
|
5899
|
-
|
|
5900
|
-
const apiListener = apiApp
|
|
5901
|
-
? createNodeListener(apiApp as never)
|
|
5902
|
-
: async () => {};
|
|
5903
|
-
|
|
5904
|
-
const gatewayListener: NodeListener = (req, res) => {
|
|
5905
|
-
const { pathname } = new URL(req.url ?? "/", ssrOrigin);
|
|
5906
|
-
return pathname === apiPrefix || pathname.startsWith(\`\${apiPrefix}/\`)
|
|
5907
|
-
? apiListener(req, res)
|
|
5908
|
-
: ssrListener(req, res);
|
|
5909
|
-
};
|
|
5910
|
-
|
|
5911
|
-
const server = createServer(gatewayListener);
|
|
6754
|
+
const server = createServer(await createListener());
|
|
5912
6755
|
|
|
5913
6756
|
server.listen(sock || port, async () => {
|
|
5914
6757
|
if (sock) {
|
|
@@ -5922,24 +6765,6 @@ export const startServer = async ({
|
|
|
5922
6765
|
return server;
|
|
5923
6766
|
};
|
|
5924
6767
|
|
|
5925
|
-
export const createDisposableServer = async (
|
|
5926
|
-
callback: (port: number) => Promise<void>,
|
|
5927
|
-
) => {
|
|
5928
|
-
const app = await createApp();
|
|
5929
|
-
const server = createAdaptorServer(app).listen(0); // OS picks a free port
|
|
5930
|
-
const address = server.address();
|
|
5931
|
-
|
|
5932
|
-
if (!address || typeof address === "string") {
|
|
5933
|
-
throw new Error("SSR: Failed starting disposable server on a free port");
|
|
5934
|
-
}
|
|
5935
|
-
|
|
5936
|
-
try {
|
|
5937
|
-
await callback(address.port);
|
|
5938
|
-
} finally {
|
|
5939
|
-
server.close();
|
|
5940
|
-
}
|
|
5941
|
-
};
|
|
5942
|
-
|
|
5943
6768
|
const isMain = fileURLToPath(import.meta.url) === resolve(process.argv[1]);
|
|
5944
6769
|
|
|
5945
6770
|
if (isMain) {
|
|
@@ -5970,14 +6795,14 @@ if (isMain) {
|
|
|
5970
6795
|
process.exit(1);
|
|
5971
6796
|
}
|
|
5972
6797
|
}
|
|
5973
|
-
`,
|
|
6798
|
+
`,Or=`string`,kr=v((e,n)=>{let{createPath:i,createImportHelpers:a}=S(e),{generators:o,refineTypeName:s,...l}=e.config,{renderToFile:u}=w({helpers:{...a({origin:`lib`})}});return{async build(e){let t=n?.renderMode?typeof n.renderMode==`string`?()=>n.renderMode:c(n?.renderMode,`string`):()=>Or,r={renderMode:JSON.stringify(n?.renderMode||null),pageRoutes:e.flatMap(e=>e.kind===`pageRoute`?[{...e.entry,renderMode:t(e.entry.name)}]:[]).sort(E),apiGenerator:o.some(e=>e.meta.slot===`backend`)};for(let[e,t]of[[`ssr.ts`,Dr],[`@ssr/__kosmo_ssr_bundle.ts`,Tr],[`@ssr/routes.ts`,Er]])await u(i.lib(e),t,r)},async postBuild(){if(!o.some(e=>e.meta.slot===`frontend`))return;let n=i.distDir(`ssr`),a=[O.tsconfigPaths(e),O.nodePrefix(),O.virtualModules(d(e,o),{kind:`ssr`,command:`build`})];await M(y(l,...o.map(({factory:t})=>t(e).config?.({kind:`client`,command:`build`})),{root:i.src(),plugins:a,build:{ssr:i.lib(`@ssr/__kosmo_ssr_bundle`),ssrEmitAssets:!0,sourcemap:!0,emptyOutDir:!0,minify:!1,copyPublicDir:!1,rolldownOptions:{output:{dir:n,entryFileNames:`app.js`,format:`esm`}}}})),await M({root:i.lib(),configFile:!1,appType:`custom`,plugins:a,resolve:{conditions:[`node`]},build:{ssr:i.lib(`ssr.ts`),target:`esnext`,sourcemap:!0,emptyOutDir:!0,rolldownOptions:{output:{dir:t(n,`server`),entryFileNames:`server.js`,format:`esm`}}}});for(let e of[`.vite`,`assets`,`index.html`])await P(r(n,`../client`,e),t(n,e),{recursive:!0});if(![!1,``].includes(l.publicDir)){let e=r(i.src(),l.publicDir||`public`);await x(e)&&await P(e,t(n,`public`),{recursive:!0})}for(let e of[`server.js`,`server.js.map`])await P(`${n}/server/${e}`,`${n}/${e}`);await F(`${n}/server`,{recursive:!0,force:!0})}}}),Ar=_({meta:{name:`SSR`,slot:`ssr`},dependencies:{tinyglobby:q.devDependencies.tinyglobby,hono:q.devDependencies.hono,"@hono/node-server":q.devDependencies[`@hono/node-server`]},factory:kr}),J={type:`module`,private:!0,name:`@kosmojs/svelte-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,"@sveltejs/vite-plugin-svelte":`^7.3.0`,vite:`^8.2.2`},devDependencies:{"@tanstack/svelte-query":`^6.1.41`,"path-to-regexp":`^8.4.2`,svelte:`^5.56.10`}},jr=()=>{let e=[`🎉 Well done! You just created a new Svelte page.`,`🚀 Success! A fresh Svelte page is ready to roll.`,`🌟 Nice work! Another Svelte page added to your app.`,`🧩 All set! A new Svelte page has been scaffolded.`,`🔧 Scaffold complete! Your new Svelte page is in place.`,`✅ Built! Your Svelte page is scaffolded and ready.`,`✨ Fantastic! Your new Svelte page is good to go.`,`🎯 Nailed it! A brand new Svelte page just landed.`,`💫 Awesome! Another Svelte page joins the party.`,`⚡ Lightning fast! A new Svelte page created successfully.`];return e[Math.floor(Math.random()*e.length)]},Y=`<script lang="ts">
|
|
5974
6799
|
import type { Snippet } from "svelte";
|
|
5975
6800
|
|
|
5976
6801
|
let { children }: { children: Snippet } = $props();
|
|
5977
6802
|
<\/script>
|
|
5978
6803
|
|
|
5979
6804
|
{@render children()}
|
|
5980
|
-
`,
|
|
6805
|
+
`,Mr=`<script lang="ts">
|
|
5981
6806
|
import { type QueryClient, QueryClientProvider } from "@tanstack/svelte-query";
|
|
5982
6807
|
import type { Snippet } from "svelte";
|
|
5983
6808
|
|
|
@@ -5994,7 +6819,7 @@ if (isMain) {
|
|
|
5994
6819
|
<QueryClientProvider client={queryClient}>
|
|
5995
6820
|
{@render children()}
|
|
5996
6821
|
</QueryClientProvider>
|
|
5997
|
-
`,
|
|
6822
|
+
`,Nr=`import { hydrate as hydrateOrig, mount as mountOrig } from "svelte";
|
|
5998
6823
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
5999
6824
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
6000
6825
|
|
|
@@ -6035,7 +6860,7 @@ export const mount = async (
|
|
|
6035
6860
|
}
|
|
6036
6861
|
|
|
6037
6862
|
export default clientRenderFactory();
|
|
6038
|
-
`,
|
|
6863
|
+
`,Pr=`import { render as renderOrig } from "svelte/server";
|
|
6039
6864
|
|
|
6040
6865
|
import type {
|
|
6041
6866
|
RenderToStringWrapper,
|
|
@@ -6101,12 +6926,18 @@ export const renderToString: RenderToStringWrapper<
|
|
|
6101
6926
|
// svelte/server exposes only render() - no web-stream renderer -
|
|
6102
6927
|
// so this folder is string-only SSR.
|
|
6103
6928
|
export default serverRenderFactory<false>();
|
|
6104
|
-
`,
|
|
6929
|
+
`,Fr=`declare module "*.svelte" {
|
|
6105
6930
|
import type { Component } from "svelte";
|
|
6106
6931
|
const component: Component;
|
|
6107
6932
|
export default component;
|
|
6108
6933
|
}
|
|
6109
|
-
|
|
6934
|
+
|
|
6935
|
+
declare module "virtual:kosmo/tsq-client" {
|
|
6936
|
+
import type { QueryClient, QueryClientConfig } from "@tanstack/svelte-query";
|
|
6937
|
+
export const createQueryClient: (options?: QueryClientConfig) => QueryClient;
|
|
6938
|
+
export const getQueryClient: () => QueryClient;
|
|
6939
|
+
}
|
|
6940
|
+
`,Ir=`<script lang="ts">
|
|
6110
6941
|
/**
|
|
6111
6942
|
* Folds [app, ...layouts] around the page component.
|
|
6112
6943
|
*
|
|
@@ -6140,7 +6971,7 @@ export default serverRenderFactory<false>();
|
|
|
6140
6971
|
{/snippet}
|
|
6141
6972
|
|
|
6142
6973
|
{@render layer(0)}
|
|
6143
|
-
`,
|
|
6974
|
+
`,Lr=`<script lang="ts">
|
|
6144
6975
|
import styles from "./styles.module.css";
|
|
6145
6976
|
|
|
6146
6977
|
let { headline }: { headline?: string } = $props();
|
|
@@ -6172,7 +7003,7 @@ export default serverRenderFactory<false>();
|
|
|
6172
7003
|
</div>
|
|
6173
7004
|
</div>
|
|
6174
7005
|
</div>
|
|
6175
|
-
`,
|
|
7006
|
+
`,Rr=`<script lang="ts">
|
|
6176
7007
|
import styles from "./styles.module.css";
|
|
6177
7008
|
|
|
6178
7009
|
let {
|
|
@@ -6217,7 +7048,7 @@ export default serverRenderFactory<false>();
|
|
|
6217
7048
|
</div>
|
|
6218
7049
|
</div>
|
|
6219
7050
|
</div>
|
|
6220
|
-
`,
|
|
7051
|
+
`,zr=`* {
|
|
6221
7052
|
margin: 0;
|
|
6222
7053
|
padding: 0;
|
|
6223
7054
|
box-sizing: border-box;
|
|
@@ -6352,7 +7183,7 @@ export default serverRenderFactory<false>();
|
|
|
6352
7183
|
align-items: center;
|
|
6353
7184
|
gap: 0.25rem;
|
|
6354
7185
|
}
|
|
6355
|
-
`,
|
|
7186
|
+
`,Br=`<script lang="ts">
|
|
6356
7187
|
import styles from "./styles.module.css";
|
|
6357
7188
|
<\/script>
|
|
6358
7189
|
|
|
@@ -6410,7 +7241,7 @@ export default serverRenderFactory<false>();
|
|
|
6410
7241
|
</div>
|
|
6411
7242
|
</div>
|
|
6412
7243
|
</div>
|
|
6413
|
-
`,
|
|
7244
|
+
`,Vr=`export type ParamsMap = {
|
|
6414
7245
|
{{#each pageRoutes}}"{{name}}": {{serializeParamsLiteral .}};
|
|
6415
7246
|
{{/each}}
|
|
6416
7247
|
};
|
|
@@ -6419,26 +7250,27 @@ export const paramNames = {
|
|
|
6419
7250
|
{{#each pageRoutes}}"{{name}}": [ {{#each params.schema}}"{{name}}", {{/each}}],
|
|
6420
7251
|
{{/each}}
|
|
6421
7252
|
} as const;
|
|
6422
|
-
`,
|
|
7253
|
+
`,Hr=`export * from "virtual:kosmo/tsq-client";
|
|
7254
|
+
`,Ur=`import { QueryClient } from "@tanstack/svelte-query";
|
|
6423
7255
|
|
|
6424
|
-
let client
|
|
7256
|
+
let client = undefined;
|
|
6425
7257
|
|
|
6426
|
-
export const createQueryClient = (options
|
|
7258
|
+
export const createQueryClient = (options) => {
|
|
6427
7259
|
client = new QueryClient(options);
|
|
6428
7260
|
return client;
|
|
6429
7261
|
};
|
|
6430
7262
|
|
|
6431
|
-
export const getQueryClient = ()
|
|
7263
|
+
export const getQueryClient = () => {
|
|
6432
7264
|
if (!client) {
|
|
6433
7265
|
client = new QueryClient();
|
|
6434
7266
|
}
|
|
6435
7267
|
return client;
|
|
6436
7268
|
};
|
|
6437
|
-
`,
|
|
7269
|
+
`,Wr=`import { QueryClient } from "@tanstack/svelte-query";
|
|
6438
7270
|
|
|
6439
|
-
import { store } from "{{ createImport '
|
|
7271
|
+
import { store } from "{{ createImport 'libCore' 'ssr' }}";
|
|
6440
7272
|
|
|
6441
|
-
export const createQueryClient = (options
|
|
7273
|
+
export const createQueryClient = (options) => {
|
|
6442
7274
|
const client = new QueryClient(options);
|
|
6443
7275
|
const ctx = store?.getStore();
|
|
6444
7276
|
if (ctx) {
|
|
@@ -6447,7 +7279,7 @@ export const createQueryClient = (options?: QueryClientConfig): QueryClient => {
|
|
|
6447
7279
|
return client;
|
|
6448
7280
|
};
|
|
6449
7281
|
|
|
6450
|
-
export const getQueryClient = ()
|
|
7282
|
+
export const getQueryClient = () => {
|
|
6451
7283
|
const ctx = store?.getStore();
|
|
6452
7284
|
if (!ctx) {
|
|
6453
7285
|
throw new Error("getQueryClient(): called outside an SSR request scope");
|
|
@@ -6455,9 +7287,9 @@ export const getQueryClient = (): QueryClient => {
|
|
|
6455
7287
|
if (!ctx.tsqClient) {
|
|
6456
7288
|
ctx.tsqClient = new QueryClient();
|
|
6457
7289
|
}
|
|
6458
|
-
return ctx.tsqClient
|
|
7290
|
+
return ctx.tsqClient;
|
|
6459
7291
|
};
|
|
6460
|
-
`,
|
|
7292
|
+
`,Gr=`import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
6461
7293
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
6462
7294
|
|
|
6463
7295
|
import Layouts from "./Layouts.svelte";
|
|
@@ -6497,7 +7329,7 @@ export default createRouterFactory<
|
|
|
6497
7329
|
Promise<RouteComponent>,
|
|
6498
7330
|
{ server: { route: Route } }
|
|
6499
7331
|
>();
|
|
6500
|
-
`,
|
|
7332
|
+
`,Kr=`import { match, pathToRegexp } from "path-to-regexp";
|
|
6501
7333
|
import { type Component, createContext } from "svelte";
|
|
6502
7334
|
|
|
6503
7335
|
import { parseSearchParams } from "@kosmojs/core";
|
|
@@ -6517,7 +7349,6 @@ export type AnyComponent = Component<any, any, any>;
|
|
|
6517
7349
|
|
|
6518
7350
|
export type RawRoute = {
|
|
6519
7351
|
name: string;
|
|
6520
|
-
pathSegments: number | undefined;
|
|
6521
7352
|
regexp: RegExp;
|
|
6522
7353
|
extractParams: (path: string) => Route["params"];
|
|
6523
7354
|
loader: () => Promise<RouteModule>;
|
|
@@ -6608,23 +7439,19 @@ export const createRouter = (
|
|
|
6608
7439
|
return {
|
|
6609
7440
|
async resolve(url: URL = new URL(window.location.href)) {
|
|
6610
7441
|
const searchParams = parseSearchParams(url);
|
|
6611
|
-
const urlSegments = url.pathname.split("/").filter(Boolean).length;
|
|
6612
|
-
|
|
6613
|
-
// 1: use lightweight \`RegExp.test()\` on linear scan - no capture allocation
|
|
6614
|
-
const matchedRoutes = routes.filter(({ regexp }) => {
|
|
6615
|
-
return regexp.test(url.pathname);
|
|
6616
|
-
});
|
|
6617
7442
|
|
|
7443
|
+
// The routes array is generated pre-sorted by specificity
|
|
7444
|
+
// (static beats required beats optional beats splat, token by token),
|
|
7445
|
+
// the same ordering the SSR server registers routes in -
|
|
7446
|
+
// so the first pattern that matches IS the most specific one, and CSR resolution stays consistent with SSR.
|
|
7447
|
+
// A route with optional parameters matches a range of segment counts,
|
|
7448
|
+
// which is why no segment-count heuristic can disambiguate here.
|
|
7449
|
+
// Lightweight \`RegExp.test()\` on linear scan - no capture allocation.
|
|
6618
7450
|
const matchedRoute =
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
: matchedRoutes.length === 1
|
|
6624
|
-
? matchedRoutes[0]
|
|
6625
|
-
: catchallRoute;
|
|
6626
|
-
|
|
6627
|
-
// 2: capture params only on matched route
|
|
7451
|
+
routes.find(({ regexp }) => {
|
|
7452
|
+
return regexp.test(url.pathname);
|
|
7453
|
+
}) || catchallRoute;
|
|
7454
|
+
|
|
6628
7455
|
const params = matchedRoute
|
|
6629
7456
|
? matchedRoute.extractParams(url.pathname)
|
|
6630
7457
|
: {};
|
|
@@ -6711,11 +7538,6 @@ export const createRoute = (
|
|
|
6711
7538
|
return {
|
|
6712
7539
|
name,
|
|
6713
7540
|
regexp,
|
|
6714
|
-
// count segments of the same base-joined path the regexp matches against;
|
|
6715
|
-
// resolve() compares this against the full url pathname's segment count
|
|
6716
|
-
pathSegments: name.includes("...")
|
|
6717
|
-
? undefined
|
|
6718
|
-
: path.split("/").filter(Boolean).length,
|
|
6719
7541
|
extractParams: (path) => {
|
|
6720
7542
|
const match = matcher(path);
|
|
6721
7543
|
return match ? match.params : {};
|
|
@@ -6724,7 +7546,7 @@ export const createRoute = (
|
|
|
6724
7546
|
layouts,
|
|
6725
7547
|
};
|
|
6726
7548
|
};
|
|
6727
|
-
`,
|
|
7549
|
+
`,qr=`import { getRouteContext } from "./svelte";
|
|
6728
7550
|
|
|
6729
7551
|
import type { ParamsMap, paramNames } from "{{ createImport 'lib' 'params' }}";
|
|
6730
7552
|
|
|
@@ -6764,7 +7586,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
6764
7586
|
const route = useRoute();
|
|
6765
7587
|
return route.loaderData?.[key || route.name] as T;
|
|
6766
7588
|
};
|
|
6767
|
-
`,
|
|
7589
|
+
`,Jr=`<script lang="ts">
|
|
6768
7590
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
6769
7591
|
import type { Snippet } from "svelte";
|
|
6770
7592
|
|
|
@@ -6774,7 +7596,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
6774
7596
|
<AppProvider>
|
|
6775
7597
|
{@render children()}
|
|
6776
7598
|
</AppProvider>
|
|
6777
|
-
`,
|
|
7599
|
+
`,Yr=`<script lang="ts">
|
|
6778
7600
|
import type { Snippet } from "svelte";
|
|
6779
7601
|
import type { HTMLAnchorAttributes } from "svelte/elements";
|
|
6780
7602
|
|
|
@@ -6798,7 +7620,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
6798
7620
|
<\/script>
|
|
6799
7621
|
|
|
6800
7622
|
<a {href} {...rest}>{@render children?.()}</a>
|
|
6801
|
-
`,
|
|
7623
|
+
`,Xr=`import renderFactory, {
|
|
6802
7624
|
createRoutes,
|
|
6803
7625
|
hydrate,
|
|
6804
7626
|
mount,
|
|
@@ -6825,7 +7647,7 @@ if (root) {
|
|
|
6825
7647
|
} else {
|
|
6826
7648
|
console.error("❌ Root element not found!");
|
|
6827
7649
|
}
|
|
6828
|
-
`,
|
|
7650
|
+
`,Zr=`import renderFactory, {
|
|
6829
7651
|
createRoutes,
|
|
6830
7652
|
renderToString,
|
|
6831
7653
|
// no renderToStream on Svelte folders
|
|
@@ -6846,31 +7668,19 @@ export default renderFactory(() => {
|
|
|
6846
7668
|
},
|
|
6847
7669
|
};
|
|
6848
7670
|
});
|
|
6849
|
-
`,
|
|
6850
|
-
<html lang="en">
|
|
6851
|
-
<head>
|
|
6852
|
-
<meta charset="UTF-8" />
|
|
6853
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6854
|
-
<!--app-head-->
|
|
6855
|
-
</head>
|
|
6856
|
-
<body>
|
|
6857
|
-
<div id="app"><!--app-html--></div>
|
|
6858
|
-
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
6859
|
-
</body>
|
|
6860
|
-
</html>
|
|
6861
|
-
`,Ar=`<script lang="ts">
|
|
7671
|
+
`,Qr=`<script lang="ts">
|
|
6862
7672
|
import PageSample from "{{ createImport 'lib' 'pageSamples/404.svelte' }}";
|
|
6863
7673
|
<\/script>
|
|
6864
7674
|
|
|
6865
7675
|
<PageSample />
|
|
6866
|
-
|
|
7676
|
+
`,$r=`<script lang="ts">
|
|
6867
7677
|
import type { Snippet } from "svelte";
|
|
6868
7678
|
|
|
6869
7679
|
let { children }: { children: Snippet } = $props();
|
|
6870
7680
|
<\/script>
|
|
6871
7681
|
|
|
6872
7682
|
{@render children()}
|
|
6873
|
-
`,
|
|
7683
|
+
`,ei=`<script lang="ts">
|
|
6874
7684
|
import PageSample from "{{ createImport 'lib' 'pageSamples/page.svelte' }}";
|
|
6875
7685
|
|
|
6876
7686
|
const pathMap = {
|
|
@@ -6889,7 +7699,7 @@ export default renderFactory(() => {
|
|
|
6889
7699
|
routeName={"{{route.name}}"}
|
|
6890
7700
|
{pathMap}
|
|
6891
7701
|
/>
|
|
6892
|
-
`,
|
|
7702
|
+
`,ti=`<script lang="ts">
|
|
6893
7703
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.svelte' }}";
|
|
6894
7704
|
<\/script>
|
|
6895
7705
|
|
|
@@ -6902,7 +7712,7 @@ export default renderFactory(() => {
|
|
|
6902
7712
|
</svelte:head>
|
|
6903
7713
|
|
|
6904
7714
|
<WelcomePage />
|
|
6905
|
-
`,
|
|
7715
|
+
`,ni=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
6906
7716
|
|
|
6907
7717
|
import app from "./app.svelte";
|
|
6908
7718
|
|
|
@@ -6917,7 +7727,7 @@ export default routerFactory((routes) => {
|
|
|
6917
7727
|
},
|
|
6918
7728
|
};
|
|
6919
7729
|
});
|
|
6920
|
-
`,
|
|
7730
|
+
`,ri=v((e,t)=>{let{createPath:n,createImportHelpers:r}=S(e),{render:i,renderToFile:a}=w({helpers:{...r({origin:`lib`}),...A()}}),{renderToFile:o}=w({helpers:r({origin:`src`})}),s=e=>!e?.trim().length,c=l(t?.templates,ei),d=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await o(n.pages(r.file),r.name===`index`?ti:c(r.name,r),{route:r,title:r.name.replace(/\{([^}]+)\}/g,`$1`),message:jr()},{overwrite:s}):t===`pageLayout`&&await o(n.pages(r.file),$r,{route:r},{overwrite:s})},f=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageLayout`?[t]:[]),r=e.flatMap(({kind:e,entry:n})=>{if(e===`pageRoute`){let{name:e,file:r}=n;return[{...n,layouts:t.flatMap(t=>t.name===e||r.startsWith(`${t.name}/`)?[t]:[]).sort(E)}]}return[]}).sort(D);for(let[e,i]of[[`client.ts`,Nr],[`server.ts`,Pr]])await a(n.libEntry(e),i,{pageRoutes:r,layouts:t});for(let[e,t]of[[`params.ts`,Vr],[`router.ts`,Gr]])await a(n.lib(e),t,{pageRoutes:r})};return{config(){let{templates:e,...n}={...t};return{plugins:fe(n)}},async start(){for(let[e,r]of[[`env.d.ts`,Fr],[`svelte.ts`,Kr],[`Layouts.svelte`,Ir],[`use.ts`,qr],[`pageSamples/styles.module.css`,zr],[`pageSamples/welcome.svelte`,Br],[`pageSamples/page.svelte`,Rr],[`pageSamples/404.svelte`,Lr],...t?.tanstack?.query?[[`app/app.svelte`,Y],[`app/app-tsq.svelte`,Mr],[`app/index.ts`,`export { default as AppProvider } from "./app-tsq.svelte";`],[`query.ts`,Hr]]:[[`app/app.svelte`,Y],[`app/app-tsq.svelte`,`/** tanstack query disabled */`],[`app/index.ts`,`export { default as AppProvider } from "./app.svelte";`],[`query.ts`,`/** tanstack query disabled */`]]])await a(n.lib(e),r,{});for(let[e,t]of[[`pages/404.svelte`,Qr],[`components/Link.svelte`,Yr],[`app.svelte`,Jr],[`router.ts`,ni]])await o(n.src(e),t,{entryDir:u.entryDir},{overwrite:s});for(let[e,t]of[[`client.ts`,Xr],[`server.ts`,Zr]])await o(n.entry(e),t,{},{overwrite:s})},async watch(e,t){await d(e.filter(g(t,[`create`]))),await f(e)},async build(e){await d(e),await f(e)},virtualModules(){return t?.tanstack?.query?[{specifier:`virtual:kosmo/tsq-client`,csr:i(Ur,{}),ssr:i(Wr,{})}]:[]}}}),ii=_({meta:{name:`Svelte`,slot:`frontend`},dependencies(e){return{svelte:J.devDependencies.svelte,...e?.tanstack?.query?{"@tanstack/svelte-query":J.devDependencies[`@tanstack/svelte-query`]}:{}}},factory:ri}),ai={type:`module`,private:!0,name:`@kosmojs/typebox-generator`,version:`0.3.0`,cacheVersion:`1`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/typebox-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,crc:`^4.3.2`,semver:`^7.8.5`},devDependencies:{"@kosmojs/core-generator":`workspace:^`,"@kosmojs/koa-generator":`workspace:^`,typebox:`^1.3.18`}},oi=`import Type from "typebox";
|
|
6921
7731
|
|
|
6922
7732
|
/**
|
|
6923
7733
|
* Custom types for JavaScript constructs that have no JSON Schema
|
|
@@ -6989,13 +7799,13 @@ export default {
|
|
|
6989
7799
|
Buffer: TBuffer(),
|
|
6990
7800
|
ArrayBuffer: TArrayBuffer(),
|
|
6991
7801
|
};
|
|
6992
|
-
`,
|
|
7802
|
+
`,si=`import type { TValidationError } from "typebox/error";
|
|
6993
7803
|
|
|
6994
7804
|
import type { ValidationErrorEntry } from "@kosmojs/core";
|
|
6995
7805
|
|
|
6996
7806
|
/**
|
|
6997
7807
|
* Message codes for i18n/l10n support
|
|
6998
|
-
*/
|
|
7808
|
+
* */
|
|
6999
7809
|
export const MESSAGE_CODES = {
|
|
7000
7810
|
// Generic messages
|
|
7001
7811
|
|
|
@@ -7185,7 +7995,7 @@ export type ValidationMessages = typeof MESSAGE_CODES;
|
|
|
7185
7995
|
*
|
|
7186
7996
|
* Usage: format(ERROR_MESSAGES[ErrorCode.STRING_MIN_LENGTH], 5)
|
|
7187
7997
|
* Result: "must be at least 5 characters long"
|
|
7188
|
-
*/
|
|
7998
|
+
* */
|
|
7189
7999
|
const MESSAGE_MAP: Record<keyof ValidationMessages, string> = {
|
|
7190
8000
|
// Generic messages
|
|
7191
8001
|
|
|
@@ -7305,7 +8115,7 @@ const MESSAGE_MAP: Record<keyof ValidationMessages, string> = {
|
|
|
7305
8115
|
* Comprehensive error handler for TypeBox validation errors.
|
|
7306
8116
|
* Supports most JSON Schema validation keywords and produces human-friendly messages
|
|
7307
8117
|
* with i18n/l10n support through message code mapping.
|
|
7308
|
-
*/
|
|
8118
|
+
* */
|
|
7309
8119
|
export const errorHandlerFactory = (
|
|
7310
8120
|
customMessages?: Partial<Record<keyof ValidationMessages, string>>,
|
|
7311
8121
|
) => {
|
|
@@ -7313,7 +8123,7 @@ export const errorHandlerFactory = (
|
|
|
7313
8123
|
|
|
7314
8124
|
/**
|
|
7315
8125
|
* Formats the instancePath into a human-readable field name
|
|
7316
|
-
*/
|
|
8126
|
+
* */
|
|
7317
8127
|
function formatFieldPath(instancePath: string, schemaPath?: string): string {
|
|
7318
8128
|
if (!instancePath?.trim?.()) {
|
|
7319
8129
|
return "root";
|
|
@@ -7351,7 +8161,7 @@ export const errorHandlerFactory = (
|
|
|
7351
8161
|
|
|
7352
8162
|
/**
|
|
7353
8163
|
* Pluralizes a word based on count
|
|
7354
|
-
*/
|
|
8164
|
+
* */
|
|
7355
8165
|
function pluralize(
|
|
7356
8166
|
count: number,
|
|
7357
8167
|
plural: string = messageMap[MESSAGE_CODES.PLURAL_SUFFIX],
|
|
@@ -7361,7 +8171,7 @@ export const errorHandlerFactory = (
|
|
|
7361
8171
|
|
|
7362
8172
|
/**
|
|
7363
8173
|
* Maps validation error to error code and formats message
|
|
7364
|
-
*/
|
|
8174
|
+
* */
|
|
7365
8175
|
function getErrorCodeAndMessage(error: TValidationError): {
|
|
7366
8176
|
code: keyof ValidationMessages;
|
|
7367
8177
|
message: string;
|
|
@@ -7781,7 +8591,7 @@ export const errorHandlerFactory = (
|
|
|
7781
8591
|
* TODO: Periodically check the TypeBox repository for implementation updates.
|
|
7782
8592
|
* Some validation cases (minContains, prefixItems, etc.) are not yet
|
|
7783
8593
|
* implemented by TypeBox, so they are commented out below.
|
|
7784
|
-
*/
|
|
8594
|
+
* */
|
|
7785
8595
|
|
|
7786
8596
|
/**
|
|
7787
8597
|
case "minContains": {
|
|
@@ -7935,7 +8745,7 @@ export const errorHandlerFactory = (
|
|
|
7935
8745
|
),
|
|
7936
8746
|
};
|
|
7937
8747
|
}
|
|
7938
|
-
|
|
8748
|
+
* */
|
|
7939
8749
|
|
|
7940
8750
|
// Default fallback
|
|
7941
8751
|
default: {
|
|
@@ -7946,7 +8756,7 @@ export const errorHandlerFactory = (
|
|
|
7946
8756
|
|
|
7947
8757
|
/**
|
|
7948
8758
|
* Groups errors by field to avoid duplicate messages
|
|
7949
|
-
*/
|
|
8759
|
+
* */
|
|
7950
8760
|
function groupErrorsByField(
|
|
7951
8761
|
errors: TValidationError[],
|
|
7952
8762
|
): Map<string, TValidationError[]> {
|
|
@@ -7965,7 +8775,7 @@ export const errorHandlerFactory = (
|
|
|
7965
8775
|
|
|
7966
8776
|
/**
|
|
7967
8777
|
* Prioritizes errors to show the most important one per field
|
|
7968
|
-
*/
|
|
8778
|
+
* */
|
|
7969
8779
|
function prioritizeErrors(errors: TValidationError[]): TValidationError {
|
|
7970
8780
|
// Priority order: required > type > format > const/enum > other constraints
|
|
7971
8781
|
const priority: Record<string, number> = {
|
|
@@ -7994,13 +8804,51 @@ export const errorHandlerFactory = (
|
|
|
7994
8804
|
})[0];
|
|
7995
8805
|
}
|
|
7996
8806
|
|
|
8807
|
+
/**
|
|
8808
|
+
* A failed union of literals arrives as one \`anyOf\` error plus one \`const\` error per branch,
|
|
8809
|
+
* all at the same instancePath.
|
|
8810
|
+
* Collapse them into a single synthetic \`enum\` error so the message reflects the whole union.
|
|
8811
|
+
* */
|
|
8812
|
+
function collapseLiteralUnion(
|
|
8813
|
+
fieldErrors: TValidationError[],
|
|
8814
|
+
): TValidationError[] {
|
|
8815
|
+
const anyOfError = fieldErrors.find((e) => e.keyword === "anyOf");
|
|
8816
|
+
if (!anyOfError) {
|
|
8817
|
+
return fieldErrors;
|
|
8818
|
+
}
|
|
8819
|
+
const branchConsts = fieldErrors.filter((e) => {
|
|
8820
|
+
return e.keyword === "const"
|
|
8821
|
+
? e.schemaPath.startsWith(\`\${anyOfError.schemaPath}/anyOf/\`)
|
|
8822
|
+
: false;
|
|
8823
|
+
});
|
|
8824
|
+
// bail unless EVERY sibling is a const branch of this anyOf -
|
|
8825
|
+
// mixed unions (literal | object) keep their current behavior
|
|
8826
|
+
if (
|
|
8827
|
+
!branchConsts.length ||
|
|
8828
|
+
fieldErrors.length !== branchConsts.length + 1
|
|
8829
|
+
) {
|
|
8830
|
+
return fieldErrors;
|
|
8831
|
+
}
|
|
8832
|
+
return [
|
|
8833
|
+
{
|
|
8834
|
+
...anyOfError,
|
|
8835
|
+
keyword: "enum",
|
|
8836
|
+
params: {
|
|
8837
|
+
allowedValues: branchConsts.map(
|
|
8838
|
+
(e) => (e.params as never)["allowedValue"],
|
|
8839
|
+
),
|
|
8840
|
+
},
|
|
8841
|
+
},
|
|
8842
|
+
];
|
|
8843
|
+
}
|
|
8844
|
+
|
|
7997
8845
|
// ============================================================================
|
|
7998
8846
|
// Public API
|
|
7999
8847
|
// ============================================================================
|
|
8000
8848
|
|
|
8001
8849
|
/**
|
|
8002
8850
|
* Main function to format validation errors
|
|
8003
|
-
*/
|
|
8851
|
+
* */
|
|
8004
8852
|
function formatValidationErrors(
|
|
8005
8853
|
errors: TValidationError[],
|
|
8006
8854
|
options: {
|
|
@@ -8035,7 +8883,7 @@ export const errorHandlerFactory = (
|
|
|
8035
8883
|
const formatted: ValidationErrorEntry[] = [];
|
|
8036
8884
|
|
|
8037
8885
|
for (const [_, fieldErrors] of grouped) {
|
|
8038
|
-
const prioritized = prioritizeErrors(fieldErrors);
|
|
8886
|
+
const prioritized = prioritizeErrors(collapseLiteralUnion(fieldErrors));
|
|
8039
8887
|
const { code, message } = getErrorCodeAndMessage(prioritized);
|
|
8040
8888
|
formatted.push({
|
|
8041
8889
|
keyword: prioritized.keyword,
|
|
@@ -8067,7 +8915,7 @@ export const errorHandlerFactory = (
|
|
|
8067
8915
|
|
|
8068
8916
|
/**
|
|
8069
8917
|
* Formats errors into a single human-readable message
|
|
8070
|
-
*/
|
|
8918
|
+
* */
|
|
8071
8919
|
function formatValidationErrorMessage(
|
|
8072
8920
|
errors: TValidationError[],
|
|
8073
8921
|
options: {
|
|
@@ -8096,7 +8944,7 @@ export const errorHandlerFactory = (
|
|
|
8096
8944
|
|
|
8097
8945
|
/**
|
|
8098
8946
|
* Gets a simple error summary for quick feedback
|
|
8099
|
-
*/
|
|
8947
|
+
* */
|
|
8100
8948
|
function getErrorSummary(errors: TValidationError[]): string {
|
|
8101
8949
|
if (!errors?.length) {
|
|
8102
8950
|
return messageMap[MESSAGE_CODES.VALIDATION_PASSED];
|
|
@@ -8173,7 +9021,7 @@ const format = (fmt: string, ...args: unknown[]): string => {
|
|
|
8173
9021
|
|
|
8174
9022
|
return str;
|
|
8175
9023
|
};
|
|
8176
|
-
`,
|
|
9024
|
+
`,ci=`import Type from "typebox";
|
|
8177
9025
|
import { Compile } from "typebox/compile";
|
|
8178
9026
|
import Value from "typebox/value";
|
|
8179
9027
|
|
|
@@ -8225,14 +9073,14 @@ export const validationSchemaFactory = (
|
|
|
8225
9073
|
},
|
|
8226
9074
|
};
|
|
8227
9075
|
};
|
|
8228
|
-
`,
|
|
9076
|
+
`,li=`import { Settings } from "typebox/system";
|
|
8229
9077
|
|
|
8230
9078
|
Settings.Set({{settings}});
|
|
8231
9079
|
|
|
8232
9080
|
export { default as customTypes } from "{{customTypesImport}}";
|
|
8233
9081
|
|
|
8234
9082
|
export const validationMessages = {{validationMessages}};
|
|
8235
|
-
`,
|
|
9083
|
+
`,ui=`import type { ValidationSchemas } from "@kosmojs/core";
|
|
8236
9084
|
|
|
8237
9085
|
import { validationSchemaFactory } from "{{ createImport 'lib' '@typebox' }}";
|
|
8238
9086
|
|
|
@@ -8293,14 +9141,14 @@ export const validationSchemas: ValidationSchemas = {
|
|
|
8293
9141
|
{{/each}}
|
|
8294
9142
|
},
|
|
8295
9143
|
};
|
|
8296
|
-
`,
|
|
9144
|
+
`,di={exactOptionalPropertyTypes:!0},fi=v((e,t)=>{let{createPath:n,createImport:r,createImportHelpers:i}=S(e),{renderToFile:a}=w({helpers:{...i({origin:`lib`})}}),{validationMessages:o={},customTypesImport:c=r.lib([`@typebox/custom-types`],{origin:`lib`}),settings:l}={...t},u=async e=>{for(let{kind:t,entry:r}of e){if(t!==`apiRoute`)continue;let e=[r.params,...r.validationDefinitions.flatMap(e=>e.target===`response`?e.variants:[e.schema])].flatMap(({resolvedType:e})=>e?[e]:[]),i=[...new Set(r.validationDefinitions.flatMap(({target:e})=>Object.keys(s).includes(e)?[e]:[]))].map(e=>({target:e,methods:r.methods.flatMap(t=>{let n=r.validationDefinitions.find(n=>n.method===t&&n.target===e);return n?[{route:r.name,method:t,target:e,schema:n.schema,...n.runtimeValidation===void 0?{}:{runtimeValidation:JSON.stringify(n.runtimeValidation)},...n.customErrors===void 0?{}:{customErrors:JSON.stringify(n.customErrors)}}]:[]})})),o=r.methods.flatMap(e=>{let t=r.validationDefinitions.find(t=>t.method===e&&t.target===`response`);return t?[{method:e,variants:t.variants.map(e=>({route:r.name,target:`response`,...e,...t.runtimeValidation===void 0?{}:{runtimeValidation:JSON.stringify(t.runtimeValidation)},...t.customErrors===void 0?{}:{customErrors:JSON.stringify(t.customErrors)}}))}]:[]});await a(n.libApi(r.name,`schemas.ts`),ui,{route:r,resolvedTypes:e,requestSchemas:i,responseSchemas:o})}};return{async start(){for(let[e,t]of[[`custom-types.ts`,oi],[`error-handler.ts`,si],[`index.ts`,ci],[`setup.ts`,li]])await a(n.lib(`@typebox`,e),t,{validationMessages:JSON.stringify(o),customTypesImport:c,settings:JSON.stringify({...di,...l})})},async watch(e,t){await u(e.filter(h(t,[`create`,`update`])))},async build(e){await u(e)}}}),X={PROPERTY:`PROPERTY`,PROPERTIES:`PROPERTIES`,ALLOWED_VALUES:`ALLOWED_VALUES`,FOUND_N_DUPLICATES:`FOUND_N_DUPLICATES`,VALIDATION_PASSED:`VALIDATION_PASSED`,VALIDATION_FAILED_PREFIX:`VALIDATION_FAILED_PREFIX`,ERROR_SUMMARY:`ERROR_SUMMARY`,PLURAL_SUFFIX:`PLURAL_SUFFIX`,FIRST:`FIRST`,SECOND:`SECOND`,THIRD:`THIRD`,FOURTH:`FOURTH`,FIFTH:`FIFTH`,TYPE_INVALID:`TYPE_INVALID`,STRING_MIN_LENGTH:`STRING_MIN_LENGTH`,STRING_MAX_LENGTH:`STRING_MAX_LENGTH`,STRING_PATTERN:`STRING_PATTERN`,STRING_FORMAT:`STRING_FORMAT`,STRING_FORMAT_EMAIL:`STRING_FORMAT_EMAIL`,STRING_FORMAT_DATE:`STRING_FORMAT_DATE`,STRING_FORMAT_DATETIME:`STRING_FORMAT_DATETIME`,STRING_FORMAT_TIME:`STRING_FORMAT_TIME`,STRING_FORMAT_URI:`STRING_FORMAT_URI`,STRING_FORMAT_URL:`STRING_FORMAT_URL`,STRING_FORMAT_UUID:`STRING_FORMAT_UUID`,STRING_FORMAT_IPV4:`STRING_FORMAT_IPV4`,STRING_FORMAT_IPV6:`STRING_FORMAT_IPV6`,STRING_FORMAT_HOSTNAME:`STRING_FORMAT_HOSTNAME`,STRING_FORMAT_JSON_POINTER:`STRING_FORMAT_JSON_POINTER`,STRING_FORMAT_REGEX:`STRING_FORMAT_REGEX`,NUMBER_MINIMUM:`NUMBER_MINIMUM`,NUMBER_MAXIMUM:`NUMBER_MAXIMUM`,NUMBER_EXCLUSIVE_MINIMUM:`NUMBER_EXCLUSIVE_MINIMUM`,NUMBER_EXCLUSIVE_MAXIMUM:`NUMBER_EXCLUSIVE_MAXIMUM`,NUMBER_MULTIPLE_OF:`NUMBER_MULTIPLE_OF`,ARRAY_MIN_ITEMS:`ARRAY_MIN_ITEMS`,ARRAY_MAX_ITEMS:`ARRAY_MAX_ITEMS`,ARRAY_UNIQUE_ITEMS:`ARRAY_UNIQUE_ITEMS`,ARRAY_CONTAINS:`ARRAY_CONTAINS`,ARRAY_MIN_CONTAINS:`ARRAY_MIN_CONTAINS`,ARRAY_MAX_CONTAINS:`ARRAY_MAX_CONTAINS`,ARRAY_PREFIX_ITEMS:`ARRAY_PREFIX_ITEMS`,ARRAY_ITEMS:`ARRAY_ITEMS`,ARRAY_UNEVALUATED_ITEMS:`ARRAY_UNEVALUATED_ITEMS`,TUPLE_MIN_ITEMS:`TUPLE_MIN_ITEMS`,TUPLE_MAX_ITEMS:`TUPLE_MAX_ITEMS`,OBJECT_REQUIRED:`OBJECT_REQUIRED`,OBJECT_ADDITIONAL_PROPERTIES:`OBJECT_ADDITIONAL_PROPERTIES`,OBJECT_MIN_PROPERTIES:`OBJECT_MIN_PROPERTIES`,OBJECT_MAX_PROPERTIES:`OBJECT_MAX_PROPERTIES`,OBJECT_PROPERTY_NAMES:`OBJECT_PROPERTY_NAMES`,OBJECT_DEPENDENCIES:`OBJECT_DEPENDENCIES`,OBJECT_UNEVALUATED_PROPERTIES:`OBJECT_UNEVALUATED_PROPERTIES`,ENUM_MISMATCH:`ENUM_MISMATCH`,CONST_MISMATCH:`CONST_MISMATCH`,CONDITIONAL_IF:`CONDITIONAL_IF`,CONDITIONAL_THEN:`CONDITIONAL_THEN`,CONDITIONAL_ELSE:`CONDITIONAL_ELSE`,COMPOSITION_ONE_OF:`COMPOSITION_ONE_OF`,COMPOSITION_ANY_OF:`COMPOSITION_ANY_OF`,COMPOSITION_ALL_OF:`COMPOSITION_ALL_OF`,COMPOSITION_NOT:`COMPOSITION_NOT`,CONTENT_DISCRIMINATOR:`CONTENT_DISCRIMINATOR`,CONTENT_ENCODING:`CONTENT_ENCODING`,CONTENT_MEDIA_TYPE:`CONTENT_MEDIA_TYPE`,CUSTOM_RANGE:`CUSTOM_RANGE`,CUSTOM_EXCLUSIVE_RANGE:`CUSTOM_EXCLUSIVE_RANGE`,CUSTOM_REGEXP:`CUSTOM_REGEXP`,CUSTOM_DYNAMIC_DEFAULTS:`CUSTOM_DYNAMIC_DEFAULTS`,CUSTOM_SELECT:`CUSTOM_SELECT`,CUSTOM_TRANSFORM:`CUSTOM_TRANSFORM`,CUSTOM_UNIQUE_ITEM_PROPERTIES:`CUSTOM_UNIQUE_ITEM_PROPERTIES`,UNKNOWN:`UNKNOWN`};X.PROPERTY,X.PROPERTIES,X.ALLOWED_VALUES,X.FOUND_N_DUPLICATES,X.VALIDATION_PASSED,X.VALIDATION_FAILED_PREFIX,X.ERROR_SUMMARY,X.PLURAL_SUFFIX,X.FIRST,X.SECOND,X.THIRD,X.FOURTH,X.FIFTH,X.TYPE_INVALID,X.STRING_MIN_LENGTH,X.STRING_MAX_LENGTH,X.STRING_PATTERN,X.STRING_FORMAT,X.STRING_FORMAT_EMAIL,X.STRING_FORMAT_DATE,X.STRING_FORMAT_DATETIME,X.STRING_FORMAT_TIME,X.STRING_FORMAT_URI,X.STRING_FORMAT_URL,X.STRING_FORMAT_UUID,X.STRING_FORMAT_IPV4,X.STRING_FORMAT_IPV6,X.STRING_FORMAT_HOSTNAME,X.STRING_FORMAT_JSON_POINTER,X.STRING_FORMAT_REGEX,X.NUMBER_MINIMUM,X.NUMBER_MAXIMUM,X.NUMBER_EXCLUSIVE_MINIMUM,X.NUMBER_EXCLUSIVE_MAXIMUM,X.NUMBER_MULTIPLE_OF,X.ARRAY_MIN_ITEMS,X.ARRAY_MAX_ITEMS,X.ARRAY_UNIQUE_ITEMS,X.ARRAY_CONTAINS,X.ARRAY_MIN_CONTAINS,X.ARRAY_MAX_CONTAINS,X.ARRAY_PREFIX_ITEMS,X.ARRAY_ITEMS,X.ARRAY_UNEVALUATED_ITEMS,X.TUPLE_MIN_ITEMS,X.TUPLE_MAX_ITEMS,X.OBJECT_REQUIRED,X.OBJECT_ADDITIONAL_PROPERTIES,X.OBJECT_MIN_PROPERTIES,X.OBJECT_MAX_PROPERTIES,X.OBJECT_PROPERTY_NAMES,X.OBJECT_DEPENDENCIES,X.OBJECT_UNEVALUATED_PROPERTIES,X.ENUM_MISMATCH,X.CONST_MISMATCH,X.CONDITIONAL_IF,X.CONDITIONAL_THEN,X.CONDITIONAL_ELSE,X.COMPOSITION_ONE_OF,X.COMPOSITION_ANY_OF,X.COMPOSITION_ALL_OF,X.COMPOSITION_NOT,X.CONTENT_DISCRIMINATOR,X.CONTENT_ENCODING,X.CONTENT_MEDIA_TYPE,X.CUSTOM_RANGE,X.CUSTOM_EXCLUSIVE_RANGE,X.CUSTOM_REGEXP,X.CUSTOM_DYNAMIC_DEFAULTS,X.CUSTOM_SELECT,X.CUSTOM_TRANSFORM,X.CUSTOM_UNIQUE_ITEM_PROPERTIES,X.UNKNOWN;var pi=_({meta:{name:`TypeBox`,resolveTypes:!0},dependencies:{typebox:ai.devDependencies.typebox},factory:fi}),Z={type:`module`,private:!0,name:`@kosmojs/vue-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{".":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`,test:`vitest --root ../.. --project generators/vue-generator`},dependencies:{"@kosmojs/core":`workspace:^`,"@kosmojs/lib":`workspace:^`,"@vitejs/plugin-vue":`^6.0.8`},devDependencies:{"@tanstack/vue-query":`^5.102.2`,vue:`^3.5.41`,"vue-router":`^5.2.0`}},Q=e=>{let t=e=>e.kind===`splat`?`:${e.name}(.*)?`:e.kind===`optional`?`:${e.name}?`:`:${e.name}`;return e.flatMap(e=>e.kind===`static`?[e.parts[0].value]:e.kind===`param`?[t(e.parts[0])]:[e.parts.map(e=>e.type===`static`?e.value:t(e)).join(``)]).join(`/`)},mi=()=>{let e=e=>e?.kind===`param`&&e.parts[0]?.kind===`splat`,n=(r,i)=>r.flatMap(({index:r,layout:a,children:o})=>{let{name:s,pathTokens:c}={...r,...a};if(!c)return[];let l=`${s}/layout`,u=i?Q(c):t(`/`,Q(c)),d=c.at(-1);return e(d)?r&&a?[{name:l,path:u,component:a.id,children:[{name:s,path:``,component:r.id}]}]:r?[{path:u,children:[{name:s,path:``,component:r.id},...n(o,s)]}]:a?[{name:l,path:u,component:a.id,children:n(o,s)}]:[]:r&&a?[{name:l,path:u,component:a.id,children:[{name:s,path:``,component:r.id},...n(o,s)]}]:r?[{path:u,children:[{name:s,path:``,component:r.id},...n(o,s)]}]:a?[{name:l,path:u,component:a.id,children:n(o,s)}]:[]});return n},hi=()=>{let e=[`🎉 Well done! You just created a new Vue route.`,`🚀 Success! A fresh Vue route is ready to roll.`,`🌟 Nice work! Another Vue route added to your app.`,`🧩 All set! A new Vue route has been scaffolded.`,`🔧 Scaffold complete! Your new Vue route is in place.`,`✅ Built! Your Vue route is scaffolded and ready.`,`✨ Fantastic! Your new Vue route is good to go.`,`🎯 Nailed it! A brand new Vue route just landed.`,`💫 Awesome! Another Vue route joins the party.`,`⚡ Lightning fast! A new Vue route created successfully.`];return e[Math.floor(Math.random()*e.length)]},gi=`import type { Plugin } from "vue";
|
|
8297
9145
|
|
|
8298
9146
|
export { default as AppProvider } from "./provider.vue";
|
|
8299
9147
|
|
|
8300
9148
|
export const appProvider: Plugin = {
|
|
8301
9149
|
install() {},
|
|
8302
9150
|
};
|
|
8303
|
-
`,
|
|
9151
|
+
`,_i=`import { VueQueryPlugin } from "@tanstack/vue-query";
|
|
8304
9152
|
import type { Plugin } from "vue";
|
|
8305
9153
|
|
|
8306
9154
|
import { getQueryClient } from "../query";
|
|
@@ -8312,10 +9160,10 @@ export const appProvider: Plugin = {
|
|
|
8312
9160
|
app.use(VueQueryPlugin, { queryClient: getQueryClient() });
|
|
8313
9161
|
},
|
|
8314
9162
|
};
|
|
8315
|
-
`,
|
|
9163
|
+
`,vi=`<template>
|
|
8316
9164
|
<slot />
|
|
8317
9165
|
</template>
|
|
8318
|
-
`,
|
|
9166
|
+
`,yi=`import type { App } from "vue";
|
|
8319
9167
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
8320
9168
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
8321
9169
|
|
|
@@ -8355,7 +9203,7 @@ export const mount = async (
|
|
|
8355
9203
|
}
|
|
8356
9204
|
|
|
8357
9205
|
export default clientRenderFactory();
|
|
8358
|
-
`,
|
|
9206
|
+
`,bi=`{
|
|
8359
9207
|
path: "{{path}}",
|
|
8360
9208
|
{{#if name}}
|
|
8361
9209
|
name: "{{name}}",
|
|
@@ -8373,7 +9221,7 @@ export default clientRenderFactory();
|
|
|
8373
9221
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
8374
9222
|
{{/if}}
|
|
8375
9223
|
}
|
|
8376
|
-
|
|
9224
|
+
`,$=`import type { App } from "vue";
|
|
8377
9225
|
|
|
8378
9226
|
import {
|
|
8379
9227
|
renderToString as renderToStringOrig,
|
|
@@ -8464,12 +9312,18 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
8464
9312
|
}
|
|
8465
9313
|
|
|
8466
9314
|
export default serverRenderFactory();
|
|
8467
|
-
|
|
9315
|
+
`,xi=`declare module "*.vue" {
|
|
8468
9316
|
import type { DefineComponent } from "vue";
|
|
8469
9317
|
const component: DefineComponent<{}, {}, any>;
|
|
8470
9318
|
export default component;
|
|
8471
9319
|
}
|
|
8472
|
-
|
|
9320
|
+
|
|
9321
|
+
declare module "virtual:kosmo/tsq-client" {
|
|
9322
|
+
import type { QueryClient, QueryClientConfig } from "@tanstack/vue-query";
|
|
9323
|
+
export const createQueryClient: (options?: QueryClientConfig) => QueryClient;
|
|
9324
|
+
export const getQueryClient: () => QueryClient;
|
|
9325
|
+
}
|
|
9326
|
+
`,Si=`<script setup lang="ts">
|
|
8473
9327
|
import styles from "./styles.module.css";
|
|
8474
9328
|
defineProps<{
|
|
8475
9329
|
headline?: string;
|
|
@@ -8508,7 +9362,7 @@ defineProps<{
|
|
|
8508
9362
|
</div>
|
|
8509
9363
|
</div>
|
|
8510
9364
|
</template>
|
|
8511
|
-
`,
|
|
9365
|
+
`,Ci=`<script setup lang="ts">
|
|
8512
9366
|
import styles from "./styles.module.css";
|
|
8513
9367
|
defineProps<{
|
|
8514
9368
|
message: string;
|
|
@@ -8557,7 +9411,7 @@ defineProps<{
|
|
|
8557
9411
|
</div>
|
|
8558
9412
|
</div>
|
|
8559
9413
|
</template>
|
|
8560
|
-
`,
|
|
9414
|
+
`,wi=`* {
|
|
8561
9415
|
margin: 0;
|
|
8562
9416
|
padding: 0;
|
|
8563
9417
|
box-sizing: border-box;
|
|
@@ -8690,7 +9544,7 @@ defineProps<{
|
|
|
8690
9544
|
align-items: center;
|
|
8691
9545
|
gap: 0.25rem;
|
|
8692
9546
|
}
|
|
8693
|
-
`,
|
|
9547
|
+
`,Ti=`<script setup lang="ts">
|
|
8694
9548
|
import styles from "./styles.module.css";
|
|
8695
9549
|
<\/script>
|
|
8696
9550
|
|
|
@@ -8754,26 +9608,27 @@ import styles from "./styles.module.css";
|
|
|
8754
9608
|
</div>
|
|
8755
9609
|
</div>
|
|
8756
9610
|
</template>
|
|
8757
|
-
`,
|
|
9611
|
+
`,Ei=`export * from "virtual:kosmo/tsq-client";
|
|
9612
|
+
`,Di=`import { QueryClient } from "@tanstack/vue-query";
|
|
8758
9613
|
|
|
8759
|
-
let client
|
|
9614
|
+
let client = undefined
|
|
8760
9615
|
|
|
8761
|
-
export const createQueryClient = (options
|
|
9616
|
+
export const createQueryClient = (options) => {
|
|
8762
9617
|
client = new QueryClient(options);
|
|
8763
9618
|
return client;
|
|
8764
9619
|
};
|
|
8765
9620
|
|
|
8766
|
-
export const getQueryClient = ()
|
|
9621
|
+
export const getQueryClient = () => {
|
|
8767
9622
|
if (!client) {
|
|
8768
9623
|
client = new QueryClient();
|
|
8769
9624
|
}
|
|
8770
9625
|
return client;
|
|
8771
9626
|
};
|
|
8772
|
-
`,
|
|
9627
|
+
`,Oi=`import { QueryClient } from "@tanstack/vue-query";
|
|
8773
9628
|
|
|
8774
|
-
import { store } from "{{ createImport '
|
|
9629
|
+
import { store } from "{{ createImport 'libCore' 'ssr' }}";
|
|
8775
9630
|
|
|
8776
|
-
export const createQueryClient = (options
|
|
9631
|
+
export const createQueryClient = (options) => {
|
|
8777
9632
|
const client = new QueryClient(options);
|
|
8778
9633
|
const ctx = store?.getStore();
|
|
8779
9634
|
if (ctx) {
|
|
@@ -8782,7 +9637,7 @@ export const createQueryClient = (options?: QueryClientConfig): QueryClient => {
|
|
|
8782
9637
|
return client;
|
|
8783
9638
|
};
|
|
8784
9639
|
|
|
8785
|
-
export const getQueryClient = ()
|
|
9640
|
+
export const getQueryClient = () => {
|
|
8786
9641
|
const ctx = store?.getStore();
|
|
8787
9642
|
if (!ctx) {
|
|
8788
9643
|
throw new Error("getQueryClient(): called outside an SSR request scope");
|
|
@@ -8790,9 +9645,9 @@ export const getQueryClient = (): QueryClient => {
|
|
|
8790
9645
|
if (!ctx.tsqClient) {
|
|
8791
9646
|
ctx.tsqClient = new QueryClient();
|
|
8792
9647
|
}
|
|
8793
|
-
return ctx.tsqClient
|
|
9648
|
+
return ctx.tsqClient;
|
|
8794
9649
|
};
|
|
8795
|
-
`,
|
|
9650
|
+
`,ki=`import {
|
|
8796
9651
|
type App,
|
|
8797
9652
|
type Component,
|
|
8798
9653
|
createApp,
|
|
@@ -8923,7 +9778,7 @@ export const createRouters = (
|
|
|
8923
9778
|
|
|
8924
9779
|
let { pathname } = url;
|
|
8925
9780
|
|
|
8926
|
-
if (base !== "/") {
|
|
9781
|
+
if ((base as string) !== "/") {
|
|
8927
9782
|
// strip the base from pushed paths
|
|
8928
9783
|
if (pathname === base) {
|
|
8929
9784
|
pathname = "/";
|
|
@@ -8954,14 +9809,14 @@ export default createRouterFactory<
|
|
|
8954
9809
|
Promise<App>,
|
|
8955
9810
|
{ server: { loaderData: Record<string, unknown> } }
|
|
8956
9811
|
>();
|
|
8957
|
-
`,
|
|
9812
|
+
`,Ai=`import { type Ref, unref } from "vue";
|
|
8958
9813
|
|
|
8959
9814
|
export type MaybeWrapped<T> = Ref<T> | T;
|
|
8960
9815
|
|
|
8961
9816
|
export function unwrap<T>(value: MaybeWrapped<T>): T {
|
|
8962
9817
|
return unref(value);
|
|
8963
9818
|
}
|
|
8964
|
-
`,
|
|
9819
|
+
`,ji=`import { useRoute, useRouter } from "vue-router";
|
|
8965
9820
|
|
|
8966
9821
|
import type { RouterWithLoaderData } from "./router";
|
|
8967
9822
|
|
|
@@ -8976,7 +9831,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
8976
9831
|
const route = useRoute();
|
|
8977
9832
|
return router.__loaderData?.[key || (route.name as string)] as T;
|
|
8978
9833
|
};
|
|
8979
|
-
`,
|
|
9834
|
+
`,Mi=`<script setup lang="ts">
|
|
8980
9835
|
import { AppProvider } from "_/app";
|
|
8981
9836
|
<\/script>
|
|
8982
9837
|
|
|
@@ -8985,7 +9840,7 @@ import { AppProvider } from "_/app";
|
|
|
8985
9840
|
<RouterView />
|
|
8986
9841
|
</AppProvider>
|
|
8987
9842
|
</template>
|
|
8988
|
-
`,
|
|
9843
|
+
`,Ni=`<script setup lang="ts" generic="T extends LinkProps">
|
|
8989
9844
|
import { computed } from "vue";
|
|
8990
9845
|
import { RouterLink } from "vue-router";
|
|
8991
9846
|
|
|
@@ -9018,7 +9873,7 @@ const linkProps = computed(() => ({
|
|
|
9018
9873
|
<slot />
|
|
9019
9874
|
</RouterLink>
|
|
9020
9875
|
</template>
|
|
9021
|
-
`,
|
|
9876
|
+
`,Pi=`import renderFactory, {
|
|
9022
9877
|
createRoutes,
|
|
9023
9878
|
hydrate,
|
|
9024
9879
|
mount,
|
|
@@ -9045,7 +9900,7 @@ if (root) {
|
|
|
9045
9900
|
} else {
|
|
9046
9901
|
console.error("❌ Root element not found!");
|
|
9047
9902
|
}
|
|
9048
|
-
`,
|
|
9903
|
+
`,Fi=`import renderFactory, {
|
|
9049
9904
|
createRoutes,
|
|
9050
9905
|
renderToStream,
|
|
9051
9906
|
renderToString,
|
|
@@ -9072,29 +9927,17 @@ export default renderFactory(() => {
|
|
|
9072
9927
|
},
|
|
9073
9928
|
};
|
|
9074
9929
|
});
|
|
9075
|
-
`,
|
|
9076
|
-
<html lang="en">
|
|
9077
|
-
<head>
|
|
9078
|
-
<meta charset="UTF-8" />
|
|
9079
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
9080
|
-
<!--app-head-->
|
|
9081
|
-
</head>
|
|
9082
|
-
<body>
|
|
9083
|
-
<div id="app"><!--app-html--></div>
|
|
9084
|
-
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
9085
|
-
</body>
|
|
9086
|
-
</html>
|
|
9087
|
-
`,mi=`<script setup lang="ts">
|
|
9930
|
+
`,Ii=`<script setup lang="ts">
|
|
9088
9931
|
import PageSample from "{{ createImport 'lib' 'pageSamples/404.vue' }}";
|
|
9089
9932
|
<\/script>
|
|
9090
9933
|
|
|
9091
9934
|
<template>
|
|
9092
9935
|
<PageSample />
|
|
9093
9936
|
</template>
|
|
9094
|
-
`,
|
|
9937
|
+
`,Li=`<template>
|
|
9095
9938
|
<router-view />
|
|
9096
9939
|
</template>
|
|
9097
|
-
`,
|
|
9940
|
+
`,Ri=`<script setup lang="ts">
|
|
9098
9941
|
import PageSample from "{{ createImport 'lib' 'pageSamples/page.vue' }}";
|
|
9099
9942
|
<\/script>
|
|
9100
9943
|
|
|
@@ -9109,14 +9952,14 @@ import PageSample from "{{ createImport 'lib' 'pageSamples/page.vue' }}";
|
|
|
9109
9952
|
}"
|
|
9110
9953
|
/>
|
|
9111
9954
|
</template>
|
|
9112
|
-
`,
|
|
9955
|
+
`,zi=`<script setup lang="ts">
|
|
9113
9956
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.vue' }}";
|
|
9114
9957
|
<\/script>
|
|
9115
9958
|
|
|
9116
9959
|
<template>
|
|
9117
9960
|
<WelcomePage />
|
|
9118
9961
|
</template>
|
|
9119
|
-
`,
|
|
9962
|
+
`,Bi=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
9120
9963
|
import { appProvider } from "{{ createImport 'lib' 'app' }}";
|
|
9121
9964
|
|
|
9122
9965
|
import app from "./app.vue";
|
|
@@ -9135,5 +9978,5 @@ export default routerFactory((routes) => {
|
|
|
9135
9978
|
},
|
|
9136
9979
|
};
|
|
9137
9980
|
});
|
|
9138
|
-
`,
|
|
9981
|
+
`,Vi=v((e,t)=>{let{createPath:n,createImportHelpers:r}=S(e),{render:i,renderToFile:a}=w({helpers:{...r({origin:`lib`}),...A()},partials:{routePartial:bi}}),{renderToFile:o}=w({helpers:r({origin:`src`})}),s=mi(),c=e=>!e?.trim().length,d=l(t?.templates,Ri),f=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await o(n.pages(r.file),r.name===`index`?zi:d(r.name,r),{route:r,message:hi()},{overwrite:c}):t===`pageLayout`&&await o(n.pages(r.file),Li,{route:r},{overwrite:c})},p=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(E),r=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),i=s(b(r));for(let[e,t]of[[`client.ts`,yi],[`server.ts`,$]])await a(n.libEntry(e),t,{pageEntries:r,nestedRoutes:i,lazyLoad:e===`client.ts`});await a(n.lib(`router.ts`),ki,{entries:e,indexRoutes:t})};return{config(){let{templates:e,...n}={...t};return{plugins:[pe(n)]}},async start(){for(let[e,r]of[[`env.d.ts`,xi],[`unwrap.ts`,Ai],[`use.ts`,ji],[`pageSamples/styles.module.css`,wi],[`pageSamples/welcome.vue`,Ti],[`pageSamples/page.vue`,Ci],[`pageSamples/404.vue`,Si],[`app/provider.vue`,vi],...t?.tanstack?.query?[[`app/index.ts`,_i],[`query.ts`,Ei]]:[[`app/index.ts`,gi],[`query.ts`,`/** tanstack query disabled */`]]])await a(n.lib(e),r,{});for(let[e,t]of[[`pages/404.vue`,Ii],[`components/Link.vue`,Ni],[`app.vue`,Mi],[`router.ts`,Bi]])await o(n.src(e),t,{entryDir:u.entryDir},{overwrite:c});for(let[e,t]of[[`client.ts`,Pi],[`server.ts`,Fi]])await o(n.entry(e),t,{},{overwrite:c})},async watch(e,t){await f(e.filter(g(t,[`create`]))),await p(e)},async build(e){await f(e),await p(e)},virtualModules(){return t?.tanstack?.query?[{specifier:`virtual:kosmo/tsq-client`,csr:i(Di,{}),ssr:i(Oi,{})}]:[]}}}),Hi=_({meta:{name:`Vue`,slot:`frontend`,jsxImportSource:`vue`},dependencies(e){return{vue:Z.devDependencies.vue,"vue-router":Z.devDependencies[`vue-router`],...e?.tanstack?.query?{"@tanstack/vue-query":Z.devDependencies[`@tanstack/vue-query`]}:{}}},factory:Vi}),Ui=e=>{let n=process.env.NODE_ENV||`development`,r=typeof e.base==`string`?e.base:e.base[n];if(!r?.trim())throw Error(i([`red`],`ERROR: Invalid Config - no base provided`));return{...e,base:t(`/`,r),apiBase:t(`/`,e.apiBase||a),generators:Wi(e)}},Wi=e=>{let t=[],n={};for(let r of e.generators||[])r.meta.slot?n[r.meta.slot]=r:t.push(r);return[B(),...n.backend?[n.backend]:[],...n.fetch&&n.backend?[n.fetch]:[],...n.frontend?[n.frontend]:[],...t,...n.ssr?[n.ssr]:[],...n.ssg?[n.ssg]:[]]};export{B as coreGenerator,Ui as defineConfig,Ae as fetchGenerator,Ze as h3Generator,_t as honoGenerator,Lt as koaGenerator,un as mdxGenerator,hn as openapiGenerator,Wn as reactGenerator,yr as solidGenerator,wr as ssgGenerator,Ar as ssrGenerator,ii as svelteGenerator,pi as typeboxGenerator,Hi as vueGenerator};
|
|
9139
9982
|
//# sourceMappingURL=index.js.map
|