@kosmojs/dev 0.2.10 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -20
- package/pkg/assets/pkg-DU-tTiIp.js +465 -0
- package/pkg/assets/pkg-DU-tTiIp.js.map +1 -0
- package/pkg/chassis.js +1 -307
- package/pkg/chassis.js.map +1 -1
- package/pkg/index.d.ts +2 -0
- package/pkg/index.js +1626 -899
- package/pkg/index.js.map +1 -1
package/pkg/index.js
CHANGED
|
@@ -1,245 +1,942 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{t as e}from"./assets/pkg-DU-tTiIp.js";import{join as t,resolve as n}from"node:path";import{styleText as r}from"node:util";import{DEFAULT_APIBASE as i,RequestBodyTargets as a,RequestValidationTargets as o,createRouteResolver as s,createTemplateResolver as c,defaults as l}from"@kosmojs/core";import{createH3Pattern as u,createHonoPattern as d,createPathPattern as f,defineGenerator as p,defineGeneratorFactory as m,mergeConfigs as h,nestedRoutesFactory as g,pathResolver as _,pathTokensFactory as v,renderFactory as y,renderToFile as ee,sortRoutes as b,spinnerFactory as te,vitePlugins as x}from"@kosmojs/lib";import{routeRenderHelpers as S}from"@kosmojs/core/generators";import C from"crc/crc32";import ne from"@mdx-js/rollup";import{build as w,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 T from"vite-plugin-solid";import{access as ce,constants as le,cp as E,mkdir as ue,rm as D,writeFile as de}from"node:fs/promises";import{svelte as fe}from"@sveltejs/vite-plugin-svelte";import pe from"@vitejs/plugin-vue";var O=`export * from "./transport";
|
|
2
|
+
`,k=`export const transport = undefined;
|
|
3
|
+
`,A=`{{#each routes}}
|
|
4
|
+
import {{id}} from "{{ createImport 'libApi' name 'fetch' }}";
|
|
5
|
+
{{/each}}
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
import type { HTTPMethod } from "@kosmojs/core/api";
|
|
5
|
-
import { createHost, type HostOpt, join, stringify } from "@kosmojs/core/fetch";
|
|
7
|
+
export { ValidationError } from "@kosmojs/core/errors";
|
|
6
8
|
|
|
7
|
-
export
|
|
9
|
+
export type ResponseT = {
|
|
10
|
+
{{#each routes}}
|
|
11
|
+
"{{name}}": import("{{ createImport 'libApi' name 'fetch' }}").ResponseT;
|
|
12
|
+
{{/each}}
|
|
13
|
+
}
|
|
8
14
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
export default {
|
|
16
|
+
{{#each routes}} "{{name}}": {{id}},
|
|
17
|
+
{{#each alias }} "{{this}}": {{../id}},
|
|
18
|
+
{{/each}}
|
|
19
|
+
{{/each}}
|
|
20
|
+
}
|
|
21
|
+
`,j=`import fetchFactory, { join } from "@kosmojs/core/fetch";
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return val;
|
|
18
|
-
}
|
|
19
|
-
const n = Number(val);
|
|
20
|
-
return Number.isFinite(n) ? n : val;
|
|
21
|
-
};
|
|
23
|
+
import { base, apiBase, apiRouteMap } from "{{ createImport 'libCore' }}";
|
|
24
|
+
import { transport } from "{{ createImport 'lib' '@fetch' }}";
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
import {
|
|
27
|
+
type MaybeWrapped,
|
|
28
|
+
unwrap,
|
|
29
|
+
} from "{{ createImport 'lib' 'unwrap' }}";
|
|
30
|
+
|
|
31
|
+
{{#each validationTypes}}
|
|
32
|
+
import type { {{id}} } from "./types";
|
|
33
|
+
{{/each}}
|
|
34
|
+
|
|
35
|
+
import { validationSchemas } from "./schemas";
|
|
36
|
+
|
|
37
|
+
export { ValidationError } from "@kosmojs/core/errors";
|
|
38
|
+
|
|
39
|
+
export { validationSchemas };
|
|
40
|
+
|
|
41
|
+
export type ParamsT = [
|
|
42
|
+
{{serializeParamsTupleElements route}}
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
{{#each payloadTypes}}
|
|
46
|
+
export type {{name}} = {
|
|
47
|
+
{{#each types}}{{method}}: {{id}};
|
|
48
|
+
{{/each}}
|
|
49
|
+
}
|
|
50
|
+
{{/each}}
|
|
51
|
+
|
|
52
|
+
export type ResponseT = {
|
|
53
|
+
{{#each responseTypes}}
|
|
54
|
+
{{method}}: {{#each types}}{{#unless @first}}|{{/unless}} {{id}}{{/each}};
|
|
55
|
+
{{/each}}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
paramsMapper,
|
|
60
|
+
parametrize,
|
|
61
|
+
payloadResolver,
|
|
62
|
+
path,
|
|
63
|
+
href,
|
|
64
|
+
} = apiRouteMap["{{route.name}}"];
|
|
65
|
+
|
|
66
|
+
const fetchApi = fetchFactory(
|
|
67
|
+
join(base, apiBase),
|
|
68
|
+
{ transport },
|
|
69
|
+
);
|
|
38
70
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
71
|
+
{{#each routeMethods}}
|
|
72
|
+
export const {{method}} = (
|
|
73
|
+
_params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,
|
|
74
|
+
_payload?: {
|
|
75
|
+
{{#each payloadTypes}}
|
|
76
|
+
{{target}}: {{id}} extends { {{method}}: unknown }
|
|
77
|
+
? MaybeWrapped<{{id}}["{{method}}"]>
|
|
78
|
+
: unknown,
|
|
79
|
+
{{/each}}
|
|
80
|
+
},
|
|
81
|
+
opt?: {
|
|
82
|
+
unwrap?: <T>(data: MaybeWrapped<T>) => T;
|
|
83
|
+
},
|
|
84
|
+
): Promise<
|
|
85
|
+
{{#if responseType}}
|
|
86
|
+
ResponseT["{{method}}"]
|
|
87
|
+
{{else}}
|
|
88
|
+
unknown
|
|
89
|
+
{{/if}}
|
|
90
|
+
> => {
|
|
91
|
+
const [params, payload] = [_params, _payload].map((data) => {
|
|
92
|
+
return typeof opt?.unwrap === "function" ? opt.unwrap(data) : unwrap(data);
|
|
93
|
+
})
|
|
94
|
+
// validate only on client, skip double validation in SSR mode
|
|
95
|
+
if (!import.meta.env.SSR) {
|
|
96
|
+
if (validationSchemas.params) {
|
|
97
|
+
validationSchemas.params.validate(
|
|
98
|
+
paramsMapper(params as never, { coerceNumbers: true }),
|
|
99
|
+
);
|
|
45
100
|
}
|
|
46
|
-
|
|
101
|
+
{{#each payloadTypes}}
|
|
102
|
+
if (validationSchemas.{{target}}?.{{method}}) {
|
|
103
|
+
validationSchemas.{{target}}.{{method}}.validate(
|
|
104
|
+
payloadResolver(payload as never, "{{target}}", "{{../method}}"),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
{{/each}}
|
|
108
|
+
}
|
|
109
|
+
return fetchApi.{{method}}(parametrize(params as never), payload as never);
|
|
110
|
+
};
|
|
111
|
+
{{/each}}
|
|
47
112
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
113
|
+
export default {
|
|
114
|
+
{{#each routeMethods}}
|
|
115
|
+
{{method}},
|
|
116
|
+
{{/each}}
|
|
117
|
+
path,
|
|
118
|
+
href,
|
|
119
|
+
validationSchemas,
|
|
120
|
+
};
|
|
121
|
+
`,M=`export type MaybeWrapped<T> = T;
|
|
122
|
+
export const unwrap = <T>(data: T) => data;
|
|
123
|
+
`,N=m(e=>{let{createPath:t,createImportHelpers:n}=_(e),{renderToFile:r}=y({helpers:{...n({origin:`lib`}),...S()}}),i=async(e,n)=>{let i=e.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]).sort(b);await r(t.lib(`fetch.ts`),A,{routes:i});for(let{kind:e,entry:i}of n)if(e===`apiRoute`){let e=[];for(let t of i.validationDefinitions)if(t.target===`response`)for(let{id:n,body:r,resolvedType:i}of t.variants)r&&e.push({id:n,target:t.target,method:t.method,resolvedType:i});else{let{id:n,resolvedType:r}=t.schema;e.push({id:n,target:t.target,method:t.method,resolvedType:r})}let n=i.methods.map(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)})),a=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 r(t.libApi(i.name,`fetch.ts`),j,{route:i,validationTypes:e,routeMethods:n,responseTypes:a})}};return{async start(){for(let[e,n]of[[`unwrap.ts`,M],[`@fetch/transport.ts`,k],[`@fetch/index.ts`,O]])await r(t.lib(e),n,{})},async watch(e,t){await i(e,t?e.filter(({kind:e,entry:n})=>t.kind===`update`&&e===`apiRoute`&&n.fileFullpath===t.file):e)},async build(e){await i(e,e)},async ssrBuild(){for(let[e,n]of[[`@fetch/transport.ts`,`export { transport } from "${l.libPrefix}/@ssr/fetch";`]])await r(t.lib(e),n,{})}}}),P=p({meta:{name:`Fetch`,slot:`fetch`},factory:N}),F={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`}},I=`import { H3, type Middleware } from "h3";
|
|
52
124
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
125
|
+
import type { Route, RouteDebugOption } from "@kosmojs/core/api";
|
|
126
|
+
|
|
127
|
+
export type App = H3;
|
|
56
128
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
129
|
+
export type AppOptions = ConstructorParameters<typeof H3>[0] & {
|
|
130
|
+
debug?: RouteDebugOption;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export function appFactory(
|
|
134
|
+
routes: Array<Route<Middleware>>,
|
|
135
|
+
options: AppOptions,
|
|
136
|
+
): App;
|
|
137
|
+
|
|
138
|
+
export function appFactory(
|
|
139
|
+
routes: Array<Route<Middleware>>,
|
|
140
|
+
fn: (a: { app: App }) => void,
|
|
141
|
+
): App;
|
|
142
|
+
|
|
143
|
+
export function appFactory(
|
|
144
|
+
routes: Array<Route<Middleware>>,
|
|
145
|
+
options: AppOptions,
|
|
146
|
+
fn: (a: { app: App }) => void,
|
|
147
|
+
): App;
|
|
148
|
+
|
|
149
|
+
export function appFactory(
|
|
150
|
+
routes: Array<Route<Middleware>>,
|
|
151
|
+
...rest: Array<unknown>
|
|
152
|
+
): App {
|
|
153
|
+
const [options, fn] = typeof rest[0] === "function" ? [{}, rest[0]] : rest;
|
|
154
|
+
|
|
155
|
+
const { debug = undefined, ...appOptions } = {
|
|
156
|
+
...(options ? { ...options } : {}),
|
|
63
157
|
};
|
|
64
158
|
|
|
65
|
-
const
|
|
66
|
-
payload: Record<ValidationTarget, T> | undefined,
|
|
67
|
-
target: ValidationTarget,
|
|
68
|
-
method: HTTPMethod,
|
|
69
|
-
) => {
|
|
70
|
-
const data = payload?.[target];
|
|
159
|
+
const app = new H3(appOptions);
|
|
71
160
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
161
|
+
if (typeof fn === "function") {
|
|
162
|
+
fn({ app });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const route of routes) {
|
|
166
|
+
if (typeof debug === "function") {
|
|
167
|
+
(debug as Function)(route.debug, route);
|
|
168
|
+
} else if (debug) {
|
|
169
|
+
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
170
|
+
}
|
|
171
|
+
for (const method of route.methods) {
|
|
172
|
+
// last middleware is the handler
|
|
173
|
+
const handler = route.middleware.at(-1);
|
|
174
|
+
if (handler) {
|
|
175
|
+
app.on(method, route.path, handler as never, {
|
|
176
|
+
middleware: route.middleware.slice(0, -1),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
85
179
|
}
|
|
180
|
+
}
|
|
86
181
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
182
|
+
return app;
|
|
183
|
+
}
|
|
184
|
+
`,L=`import type { DevSetup } from "@kosmojs/core/api";
|
|
185
|
+
|
|
186
|
+
export const devSetup = (setup: DevSetup) => setup;
|
|
187
|
+
`,R=`import type { H3Event } from "h3";
|
|
188
|
+
|
|
189
|
+
type ErrorHandler = (
|
|
190
|
+
error: any,
|
|
191
|
+
event: H3Event,
|
|
192
|
+
) => Promise<Response> | Response;
|
|
193
|
+
|
|
194
|
+
export type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;
|
|
195
|
+
|
|
196
|
+
export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
197
|
+
return handler;
|
|
198
|
+
};
|
|
199
|
+
`,z=`import { type H3Event, readBody } from "h3";
|
|
200
|
+
|
|
201
|
+
import {
|
|
202
|
+
parseCookies,
|
|
203
|
+
parseSearchParams,
|
|
204
|
+
type RequestBodyTarget,
|
|
205
|
+
type RequestMetadataTarget,
|
|
206
|
+
} from "@kosmojs/core";
|
|
207
|
+
|
|
208
|
+
export const metaparsers: {
|
|
209
|
+
[T in RequestMetadataTarget]: (event: H3Event) => unknown;
|
|
210
|
+
} = {
|
|
211
|
+
query(event) {
|
|
212
|
+
return parseSearchParams(event.url);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
headers(event) {
|
|
216
|
+
return Object.fromEntries(event.req.headers);
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
cookies(event) {
|
|
220
|
+
return parseCookies(Object.fromEntries(event.req.headers));
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export const bodyparsers: {
|
|
225
|
+
[T in RequestBodyTarget]: (event: H3Event) => Promise<unknown>;
|
|
226
|
+
} = {
|
|
227
|
+
json(event) {
|
|
228
|
+
return event.req.json();
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
form(event) {
|
|
232
|
+
return readBody(event, { type: "formData" });
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
raw(event) {
|
|
236
|
+
return event.req.text();
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
`,B=`import type { Middleware } from "h3";
|
|
240
|
+
|
|
241
|
+
import type {
|
|
242
|
+
RequestBodyTarget,
|
|
243
|
+
RequestMetadataTarget,
|
|
244
|
+
RequestValidationTarget,
|
|
245
|
+
ValidationErrorEntry,
|
|
246
|
+
} from "@kosmojs/core";
|
|
247
|
+
import {
|
|
248
|
+
type CreateRouteMiddleware,
|
|
249
|
+
createRoutes,
|
|
250
|
+
type HTTPMethod,
|
|
251
|
+
StateKey,
|
|
252
|
+
} from "@kosmojs/core/api";
|
|
253
|
+
import { ValidationError } from "@kosmojs/core/errors";
|
|
254
|
+
|
|
255
|
+
import {
|
|
256
|
+
type DefaultContext,
|
|
257
|
+
type ParameterizedEvent,
|
|
258
|
+
type ParameterizedMiddleware,
|
|
259
|
+
use,
|
|
260
|
+
} from "../api";
|
|
261
|
+
import { bodyparsers, metaparsers } from "./parsers";
|
|
262
|
+
import { routeSources } from "./routes";
|
|
263
|
+
|
|
264
|
+
import globalMiddleware from "{{ createImport 'api' 'use' }}";
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Create route-level middleware stack that handles:
|
|
268
|
+
* 1. Context extension - adds \`event.bodyparser\` (lazy, cached) and \`event.validated\` accessors
|
|
269
|
+
* 2. Params validation - normalizes and validates URL params (including splat/numeric params)
|
|
270
|
+
* 3. Request validation - validates query, headers, cookies, and body against schemas
|
|
271
|
+
* 4. Response validation - validates outgoing response against defined variants
|
|
272
|
+
*
|
|
273
|
+
* Middleware are assigned named slots (e.g. "validate:params", "validate:json")
|
|
274
|
+
* so they can be replaced by user-defined middleware in the stack.
|
|
275
|
+
*
|
|
276
|
+
* All validation errors are thrown as \`ValidationError\` instances,
|
|
277
|
+
* caught and formatted by the global error handler middleware upstream.
|
|
278
|
+
* */
|
|
279
|
+
export const createRouteMiddleware: CreateRouteMiddleware<
|
|
280
|
+
ParameterizedMiddleware
|
|
281
|
+
> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {
|
|
282
|
+
const validationMiddleware = [
|
|
283
|
+
/**
|
|
284
|
+
* Extends H3 event with:
|
|
285
|
+
*
|
|
286
|
+
* - \`event.metaparser[target]()\` - lazy, cached meta parsers.
|
|
287
|
+
* Each parser (query, headers, cookies) runs at most once per request;
|
|
288
|
+
* subsequent calls return the cached result.
|
|
289
|
+
*
|
|
290
|
+
* - \`event.bodyparser[target](opts?)\` - lazy, cached body parsers.
|
|
291
|
+
* Each parser (json, form, raw) runs at most once per request;
|
|
292
|
+
* subsequent calls return the cached result.
|
|
293
|
+
* This allows both user middleware/handlers and validators
|
|
294
|
+
* to call the same parser without re-consuming the request stream.
|
|
295
|
+
*
|
|
296
|
+
* - \`event.validated\` - getter that returns all validated data collected so far
|
|
297
|
+
* (params, query, headers, cookies, json etc.) as a plain object.
|
|
298
|
+
*
|
|
299
|
+
* Cache is stored on \`event[StateKey]\` (a Symbol-keyed Map) to keep it
|
|
300
|
+
* hidden from public API surface and serialization.
|
|
301
|
+
* */
|
|
302
|
+
use(
|
|
303
|
+
function useExtendContext(event, next) {
|
|
304
|
+
if (!event[StateKey]) {
|
|
305
|
+
// initialize per-request cache with empty params
|
|
306
|
+
// (later populated by useValidateParams)
|
|
307
|
+
event[StateKey] = new Map([["params", {}]]);
|
|
308
|
+
|
|
309
|
+
Object.defineProperty(event, "metaparser", {
|
|
310
|
+
value: Object.entries(metaparsers).reduce<{
|
|
311
|
+
[T in RequestMetadataTarget]?: () => unknown;
|
|
312
|
+
}>((map, entry) => {
|
|
313
|
+
const [target, parser] = entry as [
|
|
314
|
+
RequestMetadataTarget,
|
|
315
|
+
Function,
|
|
316
|
+
];
|
|
317
|
+
map[target] = () => {
|
|
318
|
+
if (!event[StateKey].has(target)) {
|
|
319
|
+
event[StateKey].set(
|
|
320
|
+
target,
|
|
321
|
+
target === "query"
|
|
322
|
+
? normalizeSearchParams(
|
|
323
|
+
parser(event),
|
|
324
|
+
event.req.method as never,
|
|
325
|
+
)
|
|
326
|
+
: parser(event),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
return event[StateKey].get(target);
|
|
330
|
+
};
|
|
331
|
+
return map;
|
|
332
|
+
}, {}),
|
|
333
|
+
enumerable: true,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
Object.defineProperty(event, "bodyparser", {
|
|
337
|
+
value: Object.entries(bodyparsers).reduce<{
|
|
338
|
+
[T in RequestBodyTarget]?: () => Promise<unknown>;
|
|
339
|
+
}>((map, entry) => {
|
|
340
|
+
const [target, parser] = entry as [RequestBodyTarget, Function];
|
|
341
|
+
map[target] = async () => {
|
|
342
|
+
if (!event[StateKey].has(target)) {
|
|
343
|
+
event[StateKey].set(target, await parser(event));
|
|
344
|
+
}
|
|
345
|
+
return event[StateKey].get(target);
|
|
346
|
+
};
|
|
347
|
+
return map;
|
|
348
|
+
}, {}),
|
|
349
|
+
enumerable: true,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
Object.defineProperty(event, "validated", {
|
|
353
|
+
get() {
|
|
354
|
+
return Object.fromEntries(event[StateKey]);
|
|
355
|
+
},
|
|
356
|
+
enumerable: true,
|
|
357
|
+
});
|
|
95
358
|
}
|
|
96
|
-
return
|
|
97
|
-
},
|
|
98
|
-
|
|
359
|
+
return next();
|
|
360
|
+
},
|
|
361
|
+
{ slot: "@extendContext" },
|
|
362
|
+
) as never,
|
|
99
363
|
|
|
100
|
-
|
|
101
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Normalize and validate URL params:
|
|
366
|
+
* - Splat params (e.g. \`/files{/*path}\`) are split into arrays by "/"
|
|
367
|
+
* - Numeric params are cast to Number (or array of Numbers for splat params)
|
|
368
|
+
* - Non-splat, non-numeric params pass through as strings
|
|
369
|
+
*
|
|
370
|
+
* Validated params are stored in the cache so \`event.validated.params\`
|
|
371
|
+
* reflects the normalized (and validated) values.
|
|
372
|
+
* */
|
|
373
|
+
use(
|
|
374
|
+
function useValidateParams(event, next) {
|
|
375
|
+
const normalizedParams = normalizeParams(event.url.pathname);
|
|
376
|
+
validationSchemas.params?.validate(normalizedParams);
|
|
377
|
+
event[StateKey].set("params", normalizedParams);
|
|
378
|
+
return next();
|
|
379
|
+
},
|
|
380
|
+
{ slot: "validate:params" },
|
|
381
|
+
) as never,
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Response validation - runs AFTER the handler (post-\`next()\`).
|
|
385
|
+
*
|
|
386
|
+
* Each response schema defines one or more variants, each with:
|
|
387
|
+
* - expected status code
|
|
388
|
+
* - optional content-type
|
|
389
|
+
* - optional body schema
|
|
390
|
+
*
|
|
391
|
+
* All variants are checked; if at least one passes, validation succeeds.
|
|
392
|
+
* If none pass, a ValidationError is thrown with collected errors from all variants.
|
|
393
|
+
*
|
|
394
|
+
* Activation rules:
|
|
395
|
+
* - In dev/test mode: runs unless \`runtimeValidation\` is explicitly \`false\`
|
|
396
|
+
* - In production: runs only if \`runtimeValidation\` is explicitly \`true\`
|
|
397
|
+
*
|
|
398
|
+
* Only attached to HTTP methods that have response schemas defined.
|
|
399
|
+
* */
|
|
400
|
+
use(
|
|
401
|
+
async function useValidateResponse(event, next) {
|
|
402
|
+
const variants = validationSchemas.response?.[event.req.method] || [];
|
|
403
|
+
|
|
404
|
+
if (!Array.isArray(variants) || !variants.length) {
|
|
405
|
+
return next();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// options are same for all variants
|
|
409
|
+
const { runtimeValidation, customErrors } = variants[0];
|
|
410
|
+
|
|
411
|
+
if (KOSMO_PRODUCTION_BUILD) {
|
|
412
|
+
// skip if undefined or explicitly set to false
|
|
413
|
+
if (runtimeValidation === undefined || runtimeValidation === false) {
|
|
414
|
+
return next();
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
// skip only if explicitly set to false
|
|
418
|
+
if (runtimeValidation === false) {
|
|
419
|
+
return next();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// run all downstream middleware (including the route handler)
|
|
424
|
+
const body = await next();
|
|
425
|
+
|
|
426
|
+
const response: {
|
|
427
|
+
status: number;
|
|
428
|
+
contentType: string | null;
|
|
429
|
+
body?: unknown;
|
|
430
|
+
} = {
|
|
431
|
+
status: event.res.status ?? -1,
|
|
432
|
+
contentType: event.res.headers.get("Content-Type"),
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Validate body only for JSON variants
|
|
436
|
+
if (variants.some((e) => e.contentType?.includes("json"))) {
|
|
437
|
+
response.body = body;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Returns an array of validator functions for a single response variant.
|
|
442
|
+
* Each validator checks one aspect (status, content-type, body)
|
|
443
|
+
* and returns an error entry or undefined if the check passes.
|
|
444
|
+
* */
|
|
445
|
+
const variantValidators: (
|
|
446
|
+
v: (typeof variants)[number],
|
|
447
|
+
) => Array<(i: number) => ValidationErrorEntry | undefined> = (
|
|
448
|
+
schema,
|
|
449
|
+
) => {
|
|
450
|
+
return [
|
|
451
|
+
(i) => {
|
|
452
|
+
return schema.status === response.status
|
|
453
|
+
? undefined
|
|
454
|
+
: {
|
|
455
|
+
keyword: "Status",
|
|
456
|
+
path: \`Variant #\${i}\`,
|
|
457
|
+
message: \`expected: \${schema.status}; actual: \${event.res.status}\`,
|
|
458
|
+
};
|
|
459
|
+
},
|
|
460
|
+
(i) => {
|
|
461
|
+
if (
|
|
462
|
+
!schema.contentType ||
|
|
463
|
+
schema.contentType === response.contentType
|
|
464
|
+
) {
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
keyword: "ContentType",
|
|
469
|
+
path: \`Variant #\${i}\`,
|
|
470
|
+
message: \`expected: \${schema.contentType}; actual: \${response.contentType}\`,
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
(i) => {
|
|
474
|
+
if (!schema.check || "body" in response === false) {
|
|
475
|
+
// no body schema or contentType is not JSON
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
return schema.check(response.body)
|
|
479
|
+
? undefined
|
|
480
|
+
: {
|
|
481
|
+
keyword: "Body",
|
|
482
|
+
path: \`Variant #\${i}\`,
|
|
483
|
+
message: schema.errorMessage(response.body),
|
|
484
|
+
};
|
|
485
|
+
},
|
|
486
|
+
];
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// collect errors across all variants; exit early if any variant passes
|
|
490
|
+
const errors: Array<ValidationErrorEntry> = [];
|
|
491
|
+
|
|
492
|
+
for (const [i, variant] of variants.entries()) {
|
|
493
|
+
const variantErrors = variantValidators(variant).flatMap(
|
|
494
|
+
(validator) => {
|
|
495
|
+
const error = validator(i);
|
|
496
|
+
return error ? [error] : [];
|
|
497
|
+
},
|
|
498
|
+
);
|
|
499
|
+
if (!variantErrors.length) {
|
|
500
|
+
// variant fully matched - response is valid
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
errors.push(...variantErrors);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const errorMessage = \`The response did not match any of the expected formats\`;
|
|
507
|
+
const errorSummary = \`\${variants.length} variants checked, none valid\`;
|
|
508
|
+
|
|
509
|
+
// no variant passed validation
|
|
510
|
+
throw new ValidationError([
|
|
511
|
+
"response",
|
|
512
|
+
{
|
|
513
|
+
errors,
|
|
514
|
+
errorMessage: customErrors?.error || errorMessage,
|
|
515
|
+
errorSummary,
|
|
516
|
+
route: name,
|
|
517
|
+
data: response,
|
|
518
|
+
},
|
|
519
|
+
]);
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
slot: "validate:response",
|
|
523
|
+
on: Object.keys(validationSchemas.response || {}) as Array<HTTPMethod>,
|
|
524
|
+
},
|
|
525
|
+
) as never,
|
|
526
|
+
];
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Request validation - dynamically creates one middleware per target
|
|
530
|
+
* (query, headers, cookies, json, form, multipart, raw).
|
|
531
|
+
*
|
|
532
|
+
* Each middleware:
|
|
533
|
+
* 1. Checks if a schema exists for the current HTTP method
|
|
534
|
+
* 2. Skips if \`runtimeValidation\` is explicitly disabled
|
|
535
|
+
* 3. Loads data via the appropriate source (event.query, event.headers, or event.bodyparser)
|
|
536
|
+
* 4. Validates via \`schema.validate()\` which throws on failure
|
|
537
|
+
*
|
|
538
|
+
* Body targets (json, form, raw) go through \`event.bodyparser[target]()\`,
|
|
539
|
+
* benefiting from the lazy parsing and caching set up by slot:extendContext middleware.
|
|
540
|
+
*
|
|
541
|
+
* All request validators are active on any HTTP method that has at least one
|
|
542
|
+
* schema defined across any target - this is intentionally broad to avoid
|
|
543
|
+
* silently skipping validation when methods overlap.
|
|
544
|
+
* */
|
|
545
|
+
const requestTargets: Record<
|
|
546
|
+
RequestValidationTarget,
|
|
547
|
+
(
|
|
548
|
+
event: ParameterizedEvent<Record<string, string>, DefaultContext>,
|
|
549
|
+
) => Promise<unknown>
|
|
550
|
+
> = {
|
|
551
|
+
query: async (event) => event.metaparser.query(),
|
|
552
|
+
headers: async (event) => event.metaparser.headers(),
|
|
553
|
+
cookies: async (event) => event.metaparser.cookies(),
|
|
554
|
+
json: async (event) => event.bodyparser.json(),
|
|
555
|
+
form: async (event) => event.bodyparser.form(),
|
|
556
|
+
raw: async (event) => event.bodyparser.raw(),
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const requestEntries = Object.entries(requestTargets) as Array<
|
|
560
|
+
[RequestValidationTarget, (typeof requestTargets)[RequestValidationTarget]]
|
|
561
|
+
>;
|
|
562
|
+
|
|
563
|
+
for (const [target, loadData] of requestEntries) {
|
|
564
|
+
validationMiddleware.push(
|
|
565
|
+
use(
|
|
566
|
+
async (event, next) => {
|
|
567
|
+
const schema = {
|
|
568
|
+
...validationSchemas[target]?.[event.req.method],
|
|
569
|
+
};
|
|
570
|
+
if (schema.validate && schema.runtimeValidation !== false) {
|
|
571
|
+
schema.validate(await loadData(event as never));
|
|
572
|
+
}
|
|
573
|
+
return next();
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
slot: \`validate:\${target}\`,
|
|
577
|
+
// duplicates not an issue here
|
|
578
|
+
on: requestEntries.flatMap(([target]) => {
|
|
579
|
+
return Object.keys(
|
|
580
|
+
validationSchemas[target] || {},
|
|
581
|
+
) as Array<HTTPMethod>;
|
|
582
|
+
}),
|
|
583
|
+
},
|
|
584
|
+
) as never,
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return validationMiddleware;
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
export const routes = createRoutes<ParameterizedMiddleware, Middleware>(
|
|
592
|
+
routeSources,
|
|
593
|
+
{
|
|
594
|
+
globalMiddleware: globalMiddleware as never,
|
|
595
|
+
createRouteMiddleware,
|
|
596
|
+
},
|
|
597
|
+
);
|
|
598
|
+
`,me=`import { join } from "node:path";
|
|
599
|
+
|
|
600
|
+
import type { RouteSource } from "@kosmojs/core/api";
|
|
601
|
+
|
|
602
|
+
import { base, apiBase, apiRouteMap, apiRouteMapper } from "{{ createImport 'libCore' }}";
|
|
603
|
+
|
|
604
|
+
{{#each routes}}
|
|
605
|
+
import {{id}} from "{{ createImport 'api' file }}";
|
|
606
|
+
import { validationSchemas as {{id}}_schemas } from "{{ createImport 'libApi' basename 'schemas' }}";
|
|
607
|
+
{{/each}}
|
|
608
|
+
|
|
609
|
+
{{#each cascadingMiddleware}}
|
|
610
|
+
import {{id}}, { type UseT as UseT{{id}} } from "{{ createImport 'api' file }}";
|
|
611
|
+
{{/each}}
|
|
612
|
+
|
|
613
|
+
export type RouteMap = {
|
|
614
|
+
{{#each routes}}
|
|
615
|
+
"{{name}}": {
|
|
616
|
+
paramsDefaults: {{paramsDefaults .}},
|
|
617
|
+
paramsMappings: {{paramsMappings .}},
|
|
618
|
+
cascadingState: {{cascadingState .}},
|
|
619
|
+
},
|
|
620
|
+
{{/each}}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export const routeSources: Array<RouteSource<never>> = [
|
|
624
|
+
{{#each routes}}
|
|
625
|
+
{
|
|
626
|
+
{{#if alias}}
|
|
627
|
+
...apiRouteMapper(apiBase, { ...{{serializeApiRoute .}}, pathPattern: "{{alias}}" }),
|
|
628
|
+
path: "{{alias}}",
|
|
629
|
+
pathPattern: "{{alias}}",
|
|
630
|
+
{{else}}
|
|
631
|
+
...apiRouteMap["{{name}}"],
|
|
632
|
+
path: join(base, apiBase, "{{path}}"),
|
|
633
|
+
pathPattern: join(base, apiBase, "{{pathPattern}}"),
|
|
634
|
+
{{/if}}
|
|
635
|
+
name: "{{name}}",
|
|
636
|
+
file: "{{file}}",
|
|
637
|
+
cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,
|
|
638
|
+
definitionItems: {{id}} as never,
|
|
639
|
+
validationSchemas: {{id}}_schemas,
|
|
640
|
+
},
|
|
641
|
+
{{/each}}
|
|
642
|
+
];
|
|
643
|
+
`,he=`import { parseArgs, styleText } from "node:util";
|
|
644
|
+
|
|
645
|
+
import { serve as h3serve } from "h3";
|
|
646
|
+
|
|
647
|
+
import type { App } from "./app";
|
|
648
|
+
|
|
649
|
+
type Handles = {
|
|
650
|
+
port?: number | undefined;
|
|
651
|
+
onListen?: () => Promise<void>;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const getListenHandles = async (opt?: Handles) => {
|
|
655
|
+
const { port } = opt
|
|
656
|
+
? opt
|
|
657
|
+
: parseArgs({
|
|
658
|
+
options: {
|
|
659
|
+
port: {
|
|
660
|
+
type: "string",
|
|
661
|
+
short: "p",
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
}).values;
|
|
665
|
+
|
|
666
|
+
if (![port].some(Boolean)) {
|
|
667
|
+
throw new Error("Please provide -p/--port number");
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const onListen = async () => {
|
|
671
|
+
console.log(
|
|
672
|
+
\`\\n ✨ Server Started \${styleText(["dim"], "[ %s ]")}\`,
|
|
673
|
+
\`port: \${port}\`,
|
|
674
|
+
);
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
return {
|
|
678
|
+
port: Number(port),
|
|
679
|
+
onListen: opt?.onListen || onListen,
|
|
680
|
+
};
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
684
|
+
const { port, onListen } = await getListenHandles(opt);
|
|
685
|
+
|
|
686
|
+
const server = h3serve(app, { port });
|
|
687
|
+
await server.ready().then(onListen);
|
|
688
|
+
|
|
689
|
+
return server as never;
|
|
690
|
+
};
|
|
691
|
+
`,ge=`import type { H3Event, H3EventContext } from "h3";
|
|
692
|
+
|
|
693
|
+
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
694
|
+
import {
|
|
695
|
+
use as createUse,
|
|
696
|
+
type ExtendContext,
|
|
697
|
+
type HandlerDefinition,
|
|
698
|
+
type HTTPMethod,
|
|
699
|
+
type MiddlewareDefinition,
|
|
700
|
+
type RouteDefinitionItem,
|
|
701
|
+
type UseOptions,
|
|
702
|
+
} from "@kosmojs/core/api";
|
|
703
|
+
|
|
704
|
+
import type { RouteMap } from "./@api/routes";
|
|
705
|
+
|
|
706
|
+
export interface DefaultContext extends H3EventContext {}
|
|
707
|
+
|
|
708
|
+
type MaybePromise<T = unknown> = T | Promise<T>;
|
|
709
|
+
|
|
710
|
+
type Next = () => MaybePromise<unknown | undefined>;
|
|
711
|
+
|
|
712
|
+
type ExtractBodies<R> = R extends [number, string, infer Body] ? Body : never;
|
|
713
|
+
|
|
714
|
+
type ValidatedResponseBodies<VDefs extends ValidationDefmap> = [
|
|
715
|
+
ExtractBodies<VDefs["response"]>,
|
|
716
|
+
] extends [never]
|
|
717
|
+
? unknown // No bodies extracted at all - fallback to unknown
|
|
718
|
+
: ExtractBodies<VDefs["response"]>;
|
|
719
|
+
|
|
720
|
+
export type ParameterizedEvent<
|
|
721
|
+
ParamsT,
|
|
722
|
+
ContextT,
|
|
723
|
+
VDefs extends ValidationDefmap = {},
|
|
724
|
+
VOpts extends ValidationOptmap = {},
|
|
725
|
+
> = H3Event & { context: DefaultContext } & ContextT &
|
|
726
|
+
ExtendContext<ParamsT, VDefs, VOpts>;
|
|
727
|
+
|
|
728
|
+
export type ParameterizedMiddleware<
|
|
729
|
+
ParamsT = Record<string, string>,
|
|
730
|
+
ContextT = Record<string, unknown>,
|
|
731
|
+
> = (
|
|
732
|
+
event: ParameterizedEvent<ParamsT, ContextT>,
|
|
733
|
+
next: Next,
|
|
734
|
+
) => MaybePromise<unknown>;
|
|
735
|
+
|
|
736
|
+
export type RouteHandler<
|
|
737
|
+
ParamsT,
|
|
738
|
+
ContextT,
|
|
739
|
+
VDefs extends ValidationDefmap,
|
|
740
|
+
VOpts extends ValidationOptmap = {},
|
|
741
|
+
> = (
|
|
742
|
+
event: ParameterizedEvent<ParamsT, ContextT, VDefs, VOpts>,
|
|
743
|
+
) => MaybePromise<ValidatedResponseBodies<VDefs>>;
|
|
744
|
+
|
|
745
|
+
export type DefineRouteFactory<ParamsT, ContextT> = (
|
|
746
|
+
a: {
|
|
747
|
+
// NOTE: The \`use\` helper intentionally does not accept validation types.
|
|
748
|
+
// Allowing these type parameters on \`use\` would be misleading,
|
|
749
|
+
// since middleware operates across multiple request methods with varying types.
|
|
750
|
+
use: (
|
|
751
|
+
middleware:
|
|
752
|
+
| ParameterizedMiddleware<ParamsT, ContextT>
|
|
753
|
+
| Array<ParameterizedMiddleware<ParamsT, ContextT>>,
|
|
754
|
+
options?: UseOptions,
|
|
755
|
+
) => MiddlewareDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;
|
|
756
|
+
} & {
|
|
757
|
+
[M in HTTPMethod]: <
|
|
758
|
+
VDefs extends ValidationDefmap,
|
|
759
|
+
VOpts extends ValidationOptmap = {},
|
|
760
|
+
>(
|
|
761
|
+
handler:
|
|
762
|
+
| RouteHandler<ParamsT, ContextT, VDefs, VOpts>
|
|
763
|
+
| Array<RouteHandler<ParamsT, ContextT, VDefs, VOpts>>,
|
|
764
|
+
) => HandlerDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;
|
|
765
|
+
},
|
|
766
|
+
) => Array<RouteDefinitionItem<ParameterizedMiddleware<ParamsT, ContextT>>>;
|
|
767
|
+
|
|
768
|
+
type ParamsMap<
|
|
769
|
+
Mappings extends Array<[string, unknown, boolean]>,
|
|
770
|
+
Refinements extends Array<unknown>,
|
|
771
|
+
> = {
|
|
772
|
+
[I in Extract<keyof Mappings, \`\${number}\`> as Mappings[I] extends [
|
|
773
|
+
infer ParamName extends string,
|
|
774
|
+
...Array<unknown>,
|
|
775
|
+
]
|
|
776
|
+
? ParamName
|
|
777
|
+
: never]: Mappings[I] extends [string, infer Default, true]
|
|
778
|
+
? I extends keyof Refinements
|
|
779
|
+
? Refinements[I]
|
|
780
|
+
: Default
|
|
781
|
+
: Mappings[I] extends [string, infer Default, false]
|
|
782
|
+
? I extends keyof Refinements
|
|
783
|
+
? Refinements[I] | undefined
|
|
784
|
+
: Default | undefined
|
|
785
|
+
: never;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
export const use = <ContextT = DefaultContext>(
|
|
789
|
+
middleware:
|
|
790
|
+
| ParameterizedMiddleware<Record<string, string>, ContextT>
|
|
791
|
+
| Array<ParameterizedMiddleware<Record<string, string>, ContextT>>,
|
|
792
|
+
options?: UseOptions,
|
|
793
|
+
) => {
|
|
794
|
+
return createUse<ParameterizedMiddleware<Record<string, string>, ContextT>>(
|
|
795
|
+
middleware,
|
|
796
|
+
options,
|
|
797
|
+
);
|
|
798
|
+
};
|
|
102
799
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
800
|
+
export const defineRoute: <
|
|
801
|
+
R extends keyof RouteMap,
|
|
802
|
+
ParamsD extends RouteMap[R]["paramsDefaults"] = RouteMap[R]["paramsDefaults"],
|
|
803
|
+
ContextT extends object = object,
|
|
804
|
+
>(
|
|
805
|
+
factory: DefineRouteFactory<
|
|
806
|
+
ParamsMap<RouteMap[R]["paramsMappings"], ParamsD>,
|
|
807
|
+
ContextT & RouteMap[R]["cascadingState"]
|
|
808
|
+
>,
|
|
809
|
+
) => Array<
|
|
810
|
+
RouteDefinitionItem<
|
|
811
|
+
ParameterizedMiddleware<
|
|
812
|
+
ParamsMap<RouteMap[R]["paramsMappings"], ParamsD>,
|
|
813
|
+
ContextT & RouteMap[R]["cascadingState"]
|
|
814
|
+
>
|
|
815
|
+
>
|
|
816
|
+
> = (factory) => {
|
|
817
|
+
const createHandler = <MiddlewareT>(method: HTTPMethod) => {
|
|
818
|
+
return (middleware: MiddlewareT | Array<MiddlewareT>) => {
|
|
819
|
+
return {
|
|
820
|
+
kind: "handler",
|
|
821
|
+
method,
|
|
822
|
+
middleware: [middleware].flat(),
|
|
823
|
+
};
|
|
824
|
+
};
|
|
110
825
|
};
|
|
826
|
+
return factory({
|
|
827
|
+
HEAD: createHandler("HEAD") as never,
|
|
828
|
+
OPTIONS: createHandler("OPTIONS") as never,
|
|
829
|
+
GET: createHandler("GET") as never,
|
|
830
|
+
POST: createHandler("POST") as never,
|
|
831
|
+
PUT: createHandler("PUT") as never,
|
|
832
|
+
PATCH: createHandler("PATCH") as never,
|
|
833
|
+
DELETE: createHandler("DELETE") as never,
|
|
834
|
+
// route-specific \`use\`, contains types for current route
|
|
835
|
+
use: use as never,
|
|
836
|
+
});
|
|
111
837
|
};
|
|
112
|
-
`,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
export
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{{#each routes}}
|
|
121
|
-
"{{name}}": import("{{ createImport 'libApi' name 'fetch' }}").ResponseT;
|
|
122
|
-
{{/each}}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export default {
|
|
126
|
-
{{#each routes}} "{{name}}": {{id}},
|
|
127
|
-
{{#each alias }} "{{this}}": {{../id}},
|
|
128
|
-
{{/each}}
|
|
129
|
-
{{/each}}
|
|
130
|
-
}
|
|
131
|
-
`,j=`import fetchFactory, { join, stringify } from "@kosmojs/core/fetch";
|
|
838
|
+
`,_e=`export * from "./@api/app";
|
|
839
|
+
export { appFactory as default } from "./@api/app";
|
|
840
|
+
export * from "./@api/dev";
|
|
841
|
+
export * from "./@api/errors";
|
|
842
|
+
export * from "./@api/router";
|
|
843
|
+
export * from "./@api/routes";
|
|
844
|
+
export * from "./@api/server";
|
|
845
|
+
`,ve=`import { onError } from "h3";
|
|
132
846
|
|
|
133
|
-
import {
|
|
134
|
-
import
|
|
847
|
+
import appFactory, { routes, type App } from "{{ createImport 'lib' 'api:factory' }}";
|
|
848
|
+
import defaultErrorHandler from "./errors";
|
|
135
849
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
850
|
+
export default appFactory(routes, ({ app }) => {
|
|
851
|
+
app.use(onError(defaultErrorHandler));
|
|
852
|
+
}) as App;
|
|
853
|
+
`,ye=`import { toNodeHandler } from "h3/node";
|
|
140
854
|
|
|
141
|
-
|
|
142
|
-
import type { {{id}} } from "./types";
|
|
143
|
-
{{/each}}
|
|
855
|
+
import app from "./app";
|
|
144
856
|
|
|
145
|
-
import {
|
|
857
|
+
import { devSetup } from "{{ createImport 'lib' 'api:factory' }}";
|
|
146
858
|
|
|
147
|
-
export {
|
|
859
|
+
export default devSetup({
|
|
860
|
+
requestHandler() {
|
|
861
|
+
return toNodeHandler(app);
|
|
862
|
+
},
|
|
863
|
+
teardownHandler() {
|
|
864
|
+
// close db connections, server sockets etc.
|
|
865
|
+
},
|
|
866
|
+
});
|
|
148
867
|
|
|
149
|
-
|
|
868
|
+
process.on("unhandledRejection", (reason) => {
|
|
869
|
+
console.error("💥 UNHANDLED REJECTION");
|
|
870
|
+
console.error("Reason:", reason);
|
|
871
|
+
process.exit(1);
|
|
872
|
+
});
|
|
873
|
+
`,be=`export declare module "{{ createImport 'libApi' }}" {
|
|
874
|
+
interface DefaultContext {}
|
|
875
|
+
}
|
|
876
|
+
`,xe=`import { ValidationError } from "@kosmojs/core/errors";
|
|
877
|
+
import { HTTPError } from "h3";
|
|
150
878
|
|
|
151
|
-
|
|
152
|
-
{{serializeParamsTupleElements route}}
|
|
153
|
-
];
|
|
879
|
+
import { errorHandlerFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
154
880
|
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
881
|
+
export default errorHandlerFactory(async (error, event) => {
|
|
882
|
+
const [status, message = "Unknown error occurred"] = Array.isArray(error)
|
|
883
|
+
? error
|
|
884
|
+
: error instanceof HTTPError
|
|
885
|
+
? [error.status, error.message]
|
|
886
|
+
: error instanceof ValidationError
|
|
887
|
+
? [400, \`\${error.target}: \${error.errorMessage}\`]
|
|
888
|
+
: [error.statusCode || 500, error.message];
|
|
889
|
+
|
|
890
|
+
const accept = event.req.headers.get("accept");
|
|
891
|
+
|
|
892
|
+
return accept?.includes("application/json")
|
|
893
|
+
? new Response(JSON.stringify({ error: message }), {
|
|
894
|
+
status,
|
|
895
|
+
headers: { "Content-Type": "application/json" },
|
|
896
|
+
})
|
|
897
|
+
: new Response(message, {
|
|
898
|
+
status,
|
|
899
|
+
headers: { "Content-Type": "text/plain" },
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
`,Se=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
161
903
|
|
|
162
|
-
export
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
904
|
+
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
905
|
+
GET(async (event) => {
|
|
906
|
+
return "Automatically generated route";
|
|
907
|
+
}),
|
|
908
|
+
]);
|
|
909
|
+
`,Ce=`import { use } from "{{ createImport 'libApi' }}";
|
|
167
910
|
|
|
168
|
-
|
|
169
|
-
paramsMapper,
|
|
170
|
-
parametrize,
|
|
171
|
-
payloadResolver,
|
|
172
|
-
path,
|
|
173
|
-
href,
|
|
174
|
-
} = fetchHelpers<[{{serializeParamsTupleElements route}}]>(
|
|
175
|
-
apiBase,
|
|
176
|
-
apiRouteMap["{{route.name}}"],
|
|
177
|
-
);
|
|
911
|
+
export type UseT = {};
|
|
178
912
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
)
|
|
913
|
+
export default [
|
|
914
|
+
use<UseT>(async (event, next) => {
|
|
915
|
+
return next();
|
|
916
|
+
}),
|
|
917
|
+
];
|
|
918
|
+
`,we=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
919
|
+
import app from "./app";
|
|
183
920
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
_params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,
|
|
187
|
-
_payload?: {
|
|
188
|
-
{{#each ../payloadTargets}}
|
|
189
|
-
{{#if payloadType}}
|
|
190
|
-
{{target}}: {{payloadType.name}} extends { {{../method}}: unknown }
|
|
191
|
-
? MaybeWrapped<{{payloadType.name}}["{{../method}}"]>
|
|
192
|
-
: unknown,
|
|
193
|
-
{{else}}
|
|
194
|
-
{{target}}?: unknown;
|
|
195
|
-
{{/if}}
|
|
196
|
-
{{/each}}
|
|
197
|
-
},
|
|
198
|
-
opt?: {
|
|
199
|
-
unwrap?: <T>(data: MaybeWrapped<T>) => T;
|
|
200
|
-
},
|
|
201
|
-
): Promise<
|
|
202
|
-
{{#if responseType}}
|
|
203
|
-
ResponseT["{{method}}"]
|
|
204
|
-
{{else}}
|
|
205
|
-
unknown
|
|
206
|
-
{{/if}}
|
|
207
|
-
> => {
|
|
208
|
-
const [params, payload] = [_params, _payload].map((data) => {
|
|
209
|
-
return typeof opt?.unwrap === "function" ? opt.unwrap(data) : unwrap(data);
|
|
210
|
-
})
|
|
211
|
-
// validate only on client, skip double validation in SSR mode
|
|
212
|
-
if (!import.meta.env.SSR) {
|
|
213
|
-
if (validationSchemas.params) {
|
|
214
|
-
validationSchemas.params.validate(
|
|
215
|
-
paramsMapper(params as never, { coerceNumbers: true }),
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
{{#each ../payloadTypes}}
|
|
219
|
-
if (validationSchemas.{{target}}?.{{../method}}) {
|
|
220
|
-
validationSchemas.{{target}}.{{../method}}.validate(
|
|
221
|
-
payloadResolver(payload, "{{target}}", "{{../method}}"),
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
{{/each}}
|
|
225
|
-
}
|
|
226
|
-
return fetchApi.{{method}}(parametrize(params as never), payload as never);
|
|
227
|
-
};
|
|
228
|
-
{{/each}}
|
|
921
|
+
await serve(app);
|
|
922
|
+
`,Te=`import { use } from "{{ createImport 'libApi' }}";
|
|
229
923
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
924
|
+
/**
|
|
925
|
+
* Define global middleware applied to all routes.
|
|
926
|
+
* Can be overridden on a per-route basis using the slot key.
|
|
927
|
+
* */
|
|
928
|
+
export default [
|
|
929
|
+
use(async function useExample(event, next) {
|
|
930
|
+
return next();
|
|
931
|
+
}),
|
|
932
|
+
];
|
|
933
|
+
`,Ee=m((e,n)=>{let{createPath:r,createImportHelpers:i}=_(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=y({helpers:{...i({origin:`lib`}),...S(),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}=y({helpers:i({origin:`src`})}),l=e=>e?.trim().length===0,d=c(n?.templates,Se),f=async e=>{for(let{kind:t,entry:n}of e)t===`apiRoute`?await s(r.api(n.file),d(n.name,n),{route:n},{overwrite:l}):t===`apiUse`&&await s(r.api(n.file),Ce,{},{overwrite:l})},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=v(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${C(e)}`,alias:u(n),pathTokens:n}]:[]})]}).sort(b);for(let[e,t]of[[`@api/routes.ts`,me]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{config({command:e}){return{define:{KOSMO_PRODUCTION_BUILD:e===`build`?`true`:`false`}}},async start(){for(let[e,t]of[[`api.ts`,ge],[`api:factory.ts`,_e],[`@api/app.ts`,I],[`@api/parsers.ts`,z],[`@api/dev.ts`,L],[`@api/errors.ts`,R],[`@api/router.ts`,B],[`@api/server.ts`,he]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,ve],[`dev.ts`,ye],[`errors.ts`,xe],[`server.ts`,we],[`use.ts`,Te],[`env.d.ts`,be]])await s(r.api(e),t,{},{overwrite:l})},async watch(e,t){(!t||t.kind===`create`)&&await f(e),await p(e)},async build(e){await f(e),await p(e)}}}),De=p({meta:{name:`H3`,slot:`backend`},dependencies:{h3:F.devDependencies.h3},factory:Ee}),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`}},Oe=`import { Hono, type MiddlewareHandler } from "hono";
|
|
934
|
+
import type { Router } from "hono/router";
|
|
935
|
+
import { RegExpRouter } from "hono/router/reg-exp-router";
|
|
936
|
+
import { SmartRouter } from "hono/router/smart-router";
|
|
937
|
+
import { TrieRouter } from "hono/router/trie-router";
|
|
241
938
|
|
|
242
|
-
import type {
|
|
939
|
+
import type { Route, RouteDebugOption } from "@kosmojs/core/api";
|
|
243
940
|
|
|
244
941
|
import type { DefaultBindings, DefaultVariables } from "../api";
|
|
245
942
|
|
|
@@ -250,18 +947,65 @@ export type AppEnv = {
|
|
|
250
947
|
|
|
251
948
|
export type App = Hono<AppEnv>;
|
|
252
949
|
|
|
253
|
-
export type AppOptions = ConstructorParameters<typeof Hono<AppEnv>>[0]
|
|
950
|
+
export type AppOptions = ConstructorParameters<typeof Hono<AppEnv>>[0] & {
|
|
951
|
+
debug?: RouteDebugOption;
|
|
952
|
+
};
|
|
254
953
|
|
|
255
|
-
export
|
|
256
|
-
|
|
257
|
-
|
|
954
|
+
export function appFactory(
|
|
955
|
+
routes: Array<Route<MiddlewareHandler>>,
|
|
956
|
+
options: AppOptions,
|
|
957
|
+
): App;
|
|
958
|
+
|
|
959
|
+
export function appFactory(
|
|
960
|
+
routes: Array<Route<MiddlewareHandler>>,
|
|
961
|
+
fn: (a: { app: App; router: Router<never> }) => void,
|
|
962
|
+
): App;
|
|
963
|
+
|
|
964
|
+
export function appFactory(
|
|
965
|
+
routes: Array<Route<MiddlewareHandler>>,
|
|
966
|
+
options: AppOptions,
|
|
967
|
+
fn: (a: { app: App; router: Router<never> }) => void,
|
|
968
|
+
): App;
|
|
969
|
+
|
|
970
|
+
export function appFactory(
|
|
971
|
+
routes: Array<Route<MiddlewareHandler>>,
|
|
972
|
+
...rest: Array<unknown>
|
|
973
|
+
): App {
|
|
974
|
+
const [options, fn] = typeof rest[0] === "function" ? [{}, rest[0]] : rest;
|
|
975
|
+
|
|
976
|
+
const router = new SmartRouter({
|
|
977
|
+
routers: [new RegExpRouter(), new TrieRouter()],
|
|
978
|
+
}) as Router<never>;
|
|
979
|
+
|
|
980
|
+
const { debug = undefined, ...appOptions } = {
|
|
981
|
+
...(options ? { ...options } : {}),
|
|
258
982
|
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
983
|
+
|
|
984
|
+
const app = new Hono({
|
|
985
|
+
strict: false,
|
|
986
|
+
router,
|
|
987
|
+
...appOptions,
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
if (typeof fn === "function") {
|
|
991
|
+
fn({ app, router });
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
for (const route of routes) {
|
|
995
|
+
if (typeof debug === "function") {
|
|
996
|
+
(debug as Function)(route.debug, route);
|
|
997
|
+
} else if (debug) {
|
|
998
|
+
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
999
|
+
}
|
|
1000
|
+
app.on(route.methods, [route.path], ...route.middleware);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return app as never;
|
|
1004
|
+
}
|
|
1005
|
+
`,ke=`import type { DevSetup } from "@kosmojs/core/api";
|
|
262
1006
|
|
|
263
1007
|
export const devSetup = (setup: DevSetup) => setup;
|
|
264
|
-
`,
|
|
1008
|
+
`,Ae=`import type { Context } from "hono";
|
|
265
1009
|
|
|
266
1010
|
import type { AppEnv } from "./app";
|
|
267
1011
|
|
|
@@ -275,10 +1019,14 @@ export type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;
|
|
|
275
1019
|
export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
276
1020
|
return handler;
|
|
277
1021
|
};
|
|
278
|
-
`,
|
|
1022
|
+
`,je=`import type { Context } from "hono";
|
|
279
1023
|
|
|
280
|
-
import
|
|
281
|
-
|
|
1024
|
+
import {
|
|
1025
|
+
parseCookies,
|
|
1026
|
+
parseSearchParams,
|
|
1027
|
+
type RequestBodyTarget,
|
|
1028
|
+
type RequestMetadataTarget,
|
|
1029
|
+
} from "@kosmojs/core";
|
|
282
1030
|
|
|
283
1031
|
export type BodyparserOptions = {
|
|
284
1032
|
json: never;
|
|
@@ -290,7 +1038,7 @@ export const metaparsers: {
|
|
|
290
1038
|
[T in RequestMetadataTarget]: (ctx: Context) => unknown;
|
|
291
1039
|
} = {
|
|
292
1040
|
query(ctx) {
|
|
293
|
-
return
|
|
1041
|
+
return parseSearchParams(ctx.req.url);
|
|
294
1042
|
},
|
|
295
1043
|
|
|
296
1044
|
headers(ctx) {
|
|
@@ -320,12 +1068,7 @@ export const bodyparsers: {
|
|
|
320
1068
|
return ctx.req[as]();
|
|
321
1069
|
},
|
|
322
1070
|
};
|
|
323
|
-
`,
|
|
324
|
-
import type { Router } from "hono/router";
|
|
325
|
-
import { RegExpRouter } from "hono/router/reg-exp-router";
|
|
326
|
-
import { SmartRouter } from "hono/router/smart-router";
|
|
327
|
-
import { TrieRouter } from "hono/router/trie-router";
|
|
328
|
-
import { match } from "path-to-regexp";
|
|
1071
|
+
`,Me=`import type { MiddlewareHandler } from "hono";
|
|
329
1072
|
|
|
330
1073
|
import type {
|
|
331
1074
|
RequestBodyTarget,
|
|
@@ -337,7 +1080,6 @@ import {
|
|
|
337
1080
|
type CreateRouteMiddleware,
|
|
338
1081
|
createRoutes,
|
|
339
1082
|
type HTTPMethod,
|
|
340
|
-
type RouterFactory,
|
|
341
1083
|
StateKey,
|
|
342
1084
|
} from "@kosmojs/core/api";
|
|
343
1085
|
import { ValidationError } from "@kosmojs/core/errors";
|
|
@@ -353,7 +1095,6 @@ import { type BodyparserOptions, bodyparsers, metaparsers } from "./parsers";
|
|
|
353
1095
|
import { routeSources } from "./routes";
|
|
354
1096
|
|
|
355
1097
|
import globalMiddleware from "{{ createImport 'api' 'use' }}";
|
|
356
|
-
import { apiRouteMap } from "{{ createImport 'libCore' }}";
|
|
357
1098
|
|
|
358
1099
|
/**
|
|
359
1100
|
* Create route-level middleware stack that handles:
|
|
@@ -370,33 +1111,7 @@ import { apiRouteMap } from "{{ createImport 'libCore' }}";
|
|
|
370
1111
|
* */
|
|
371
1112
|
export const createRouteMiddleware: CreateRouteMiddleware<
|
|
372
1113
|
ParameterizedMiddleware
|
|
373
|
-
> = ({ name,
|
|
374
|
-
const route = apiRouteMap[name];
|
|
375
|
-
|
|
376
|
-
if (!route) {
|
|
377
|
-
throw new Error(\`createRouteMiddleware: \${name} route does not exists\`);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const { params, numericProperties } = route;
|
|
381
|
-
|
|
382
|
-
const pathMatcher = match(pathPattern);
|
|
383
|
-
|
|
384
|
-
const matchPath = (path: string) => {
|
|
385
|
-
try {
|
|
386
|
-
return pathMatcher(path);
|
|
387
|
-
} catch (_e) {
|
|
388
|
-
return undefined;
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
const maybeNumber = (val: unknown) => {
|
|
393
|
-
if (val === undefined || val === null) {
|
|
394
|
-
return val;
|
|
395
|
-
}
|
|
396
|
-
const n = Number(val);
|
|
397
|
-
return Number.isFinite(n) ? n : val;
|
|
398
|
-
};
|
|
399
|
-
|
|
1114
|
+
> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {
|
|
400
1115
|
const validationMiddleware = [
|
|
401
1116
|
/**
|
|
402
1117
|
* Extends Hono context with:
|
|
@@ -437,15 +1152,9 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
437
1152
|
ctx[StateKey].set(
|
|
438
1153
|
target,
|
|
439
1154
|
target === "query"
|
|
440
|
-
?
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
numericProperties.query[ctx.req.method]?.includes(k)
|
|
444
|
-
? Array.isArray(v)
|
|
445
|
-
? v.map((e) => maybeNumber(e))
|
|
446
|
-
: maybeNumber(v)
|
|
447
|
-
: v,
|
|
448
|
-
]),
|
|
1155
|
+
? normalizeSearchParams(
|
|
1156
|
+
parser(ctx),
|
|
1157
|
+
ctx.req.method as never,
|
|
449
1158
|
)
|
|
450
1159
|
: parser(ctx),
|
|
451
1160
|
);
|
|
@@ -498,23 +1207,7 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
498
1207
|
* */
|
|
499
1208
|
use(
|
|
500
1209
|
function useValidateParams(ctx, next) {
|
|
501
|
-
const
|
|
502
|
-
const normalizedParams = params.reduce(
|
|
503
|
-
(map: Record<string, unknown>, name) => {
|
|
504
|
-
const value = matched ? matched.params[name] : undefined;
|
|
505
|
-
if (Array.isArray(value)) {
|
|
506
|
-
map[name] = numericProperties.params.includes(name)
|
|
507
|
-
? value.map((e) => maybeNumber(e))
|
|
508
|
-
: value;
|
|
509
|
-
} else if (value) {
|
|
510
|
-
map[name] = numericProperties.params.includes(name)
|
|
511
|
-
? maybeNumber(value)
|
|
512
|
-
: value;
|
|
513
|
-
}
|
|
514
|
-
return map;
|
|
515
|
-
},
|
|
516
|
-
{},
|
|
517
|
-
);
|
|
1210
|
+
const normalizedParams = normalizeParams(ctx.req.path);
|
|
518
1211
|
validationSchemas.params?.validate(normalizedParams);
|
|
519
1212
|
ctx[StateKey].set("params", normalizedParams);
|
|
520
1213
|
return next();
|
|
@@ -742,32 +1435,21 @@ export const routes = createRoutes<ParameterizedMiddleware, MiddlewareHandler>(
|
|
|
742
1435
|
createRouteMiddleware,
|
|
743
1436
|
},
|
|
744
1437
|
);
|
|
745
|
-
|
|
746
|
-
export const routerFactory: RouterFactory<Router<never>, never> = (factory) => {
|
|
747
|
-
const createRouter = () => {
|
|
748
|
-
return new SmartRouter({
|
|
749
|
-
routers: [new RegExpRouter(), new TrieRouter()],
|
|
750
|
-
}) as Router<never>;
|
|
751
|
-
};
|
|
752
|
-
return factory({ createRouter });
|
|
753
|
-
};
|
|
754
|
-
`,V=`import { join } from "node:path";
|
|
1438
|
+
`,Ne=`import { join } from "node:path";
|
|
755
1439
|
|
|
756
1440
|
import type { RouteSource } from "@kosmojs/core/api";
|
|
757
1441
|
|
|
758
|
-
import { base, apiBase } from "{{ createImport 'libCore' }}";
|
|
1442
|
+
import { base, apiBase, apiRouteMap, apiRouteMapper } from "{{ createImport 'libCore' }}";
|
|
759
1443
|
|
|
760
1444
|
{{#each routes}}
|
|
761
1445
|
import {{id}} from "{{ createImport 'api' file }}";
|
|
762
|
-
import { validationSchemas as {{id}}_schemas } from "{{ createImport 'libApi'
|
|
1446
|
+
import { validationSchemas as {{id}}_schemas } from "{{ createImport 'libApi' basename 'schemas' }}";
|
|
763
1447
|
{{/each}}
|
|
764
1448
|
|
|
765
1449
|
{{#each cascadingMiddleware}}
|
|
766
1450
|
import {{id}}, { type UseT as UseT{{id}} } from "{{ createImport 'api' file }}";
|
|
767
1451
|
{{/each}}
|
|
768
1452
|
|
|
769
|
-
type Override<A, B> = Omit<A, keyof B> & B;
|
|
770
|
-
|
|
771
1453
|
export type RouteMap = {
|
|
772
1454
|
{{#each routes}}
|
|
773
1455
|
"{{name}}": {
|
|
@@ -781,14 +1463,16 @@ export type RouteMap = {
|
|
|
781
1463
|
export const routeSources: Array<RouteSource<never>> = [
|
|
782
1464
|
{{#each routes}}
|
|
783
1465
|
{
|
|
784
|
-
|
|
785
|
-
{{
|
|
786
|
-
path: "{{
|
|
787
|
-
pathPattern: "{{
|
|
1466
|
+
{{#if alias}}
|
|
1467
|
+
...apiRouteMapper(apiBase, { ...{{serializeApiRoute .}}, pathPattern: "{{alias}}" }),
|
|
1468
|
+
path: "{{alias}}",
|
|
1469
|
+
pathPattern: "{{alias}}",
|
|
788
1470
|
{{else}}
|
|
1471
|
+
...apiRouteMap["{{name}}"],
|
|
789
1472
|
path: join(base, apiBase, "{{path}}"),
|
|
790
1473
|
pathPattern: join(base, apiBase, "{{pathPattern}}"),
|
|
791
1474
|
{{/if}}
|
|
1475
|
+
name: "{{name}}",
|
|
792
1476
|
file: "{{file}}",
|
|
793
1477
|
cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,
|
|
794
1478
|
definitionItems: {{id}} as never,
|
|
@@ -796,52 +1480,48 @@ export const routeSources: Array<RouteSource<never>> = [
|
|
|
796
1480
|
},
|
|
797
1481
|
{{/each}}
|
|
798
1482
|
];
|
|
799
|
-
`,
|
|
1483
|
+
`,Pe=`import { chmod, unlink } from "node:fs/promises";
|
|
800
1484
|
import { parseArgs, styleText } from "node:util";
|
|
801
1485
|
|
|
802
1486
|
import { createAdaptorServer } from "@hono/node-server";
|
|
803
1487
|
|
|
804
|
-
import type { ServerFactory } from "@kosmojs/core/api";
|
|
805
|
-
|
|
806
1488
|
import type { App } from "./app";
|
|
807
1489
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
short: "p",
|
|
814
|
-
},
|
|
815
|
-
sock: {
|
|
816
|
-
type: "string",
|
|
817
|
-
short: "s",
|
|
818
|
-
},
|
|
819
|
-
},
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
const getListenHandles = async () => {
|
|
823
|
-
const { port, sock } = { ...values };
|
|
1490
|
+
type Handles = {
|
|
1491
|
+
port?: number | undefined;
|
|
1492
|
+
sock?: string | undefined;
|
|
1493
|
+
onListen?: () => Promise<void>;
|
|
1494
|
+
};
|
|
824
1495
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1496
|
+
const getListenHandles = async (opt?: Handles) => {
|
|
1497
|
+
const { port, sock } = opt
|
|
1498
|
+
? opt
|
|
1499
|
+
: parseArgs({
|
|
1500
|
+
options: {
|
|
1501
|
+
port: {
|
|
1502
|
+
type: "string",
|
|
1503
|
+
short: "p",
|
|
1504
|
+
},
|
|
1505
|
+
sock: {
|
|
1506
|
+
type: "string",
|
|
1507
|
+
short: "s",
|
|
1508
|
+
},
|
|
1509
|
+
},
|
|
1510
|
+
}).values;
|
|
829
1511
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
console.error(error.message);
|
|
836
|
-
process.exit(1);
|
|
837
|
-
});
|
|
838
|
-
}
|
|
1512
|
+
if (![port, sock].some(Boolean)) {
|
|
1513
|
+
throw new Error("Please provide either -p/--port number or -s/--sock path");
|
|
1514
|
+
}
|
|
839
1515
|
|
|
840
|
-
|
|
841
|
-
|
|
1516
|
+
if (sock) {
|
|
1517
|
+
await unlink(sock).catch((error) => {
|
|
1518
|
+
if (error.code !== "ENOENT") {
|
|
1519
|
+
throw error;
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
842
1523
|
|
|
843
1524
|
const onListen = async () => {
|
|
844
|
-
const { port, sock } = await getListenHandles();
|
|
845
1525
|
if (sock) {
|
|
846
1526
|
// Make Unix socket world-writable so other processes (e.g. a reverse proxy)
|
|
847
1527
|
// can connect without permission issues.
|
|
@@ -853,50 +1533,39 @@ export const serverFactory: ServerFactory<App> = (factory) => {
|
|
|
853
1533
|
);
|
|
854
1534
|
};
|
|
855
1535
|
|
|
856
|
-
return
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
sock
|
|
863
|
-
? { unix: sock, fetch: app.fetch }
|
|
864
|
-
: { port: Number(port), fetch: app.fetch },
|
|
865
|
-
);
|
|
866
|
-
await onListen();
|
|
867
|
-
return server as never;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
if (typeof Deno !== "undefined") {
|
|
871
|
-
const server = sock
|
|
872
|
-
? Deno.serve({ path: sock, onListen }, app.fetch)
|
|
873
|
-
: Deno.serve({ port: Number(port), onListen }, app.fetch);
|
|
874
|
-
return server as never;
|
|
875
|
-
}
|
|
1536
|
+
return {
|
|
1537
|
+
port: Number(port),
|
|
1538
|
+
sock,
|
|
1539
|
+
onListen: opt?.onListen || onListen,
|
|
1540
|
+
};
|
|
1541
|
+
};
|
|
876
1542
|
|
|
877
|
-
|
|
878
|
-
|
|
1543
|
+
export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
1544
|
+
const { port, sock, onListen } = await getListenHandles(opt);
|
|
879
1545
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1546
|
+
if (typeof Bun !== "undefined") {
|
|
1547
|
+
const server = Bun.serve(
|
|
1548
|
+
sock
|
|
1549
|
+
? { unix: sock, fetch: app.fetch }
|
|
1550
|
+
: { port: Number(port), fetch: app.fetch },
|
|
1551
|
+
);
|
|
1552
|
+
await onListen();
|
|
1553
|
+
return server as never;
|
|
1554
|
+
}
|
|
886
1555
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
console.error("Reason:", reason);
|
|
893
|
-
console.error("");
|
|
894
|
-
// In development, crash hard
|
|
895
|
-
if (process.env.NODE_ENV === "development") {
|
|
896
|
-
process.exit(1);
|
|
1556
|
+
if (typeof Deno !== "undefined") {
|
|
1557
|
+
const server = sock
|
|
1558
|
+
? Deno.serve({ path: sock, onListen }, app.fetch)
|
|
1559
|
+
: Deno.serve({ port: Number(port), onListen }, app.fetch);
|
|
1560
|
+
return server as never;
|
|
897
1561
|
}
|
|
898
|
-
|
|
899
|
-
|
|
1562
|
+
|
|
1563
|
+
const server = createAdaptorServer(app);
|
|
1564
|
+
server.listen(sock || port, onListen);
|
|
1565
|
+
|
|
1566
|
+
return server as never;
|
|
1567
|
+
};
|
|
1568
|
+
`,Fe=`import type { Context, Next } from "hono";
|
|
900
1569
|
|
|
901
1570
|
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
902
1571
|
import {
|
|
@@ -1079,29 +1748,20 @@ export const defineRoute: <
|
|
|
1079
1748
|
use: use as never,
|
|
1080
1749
|
});
|
|
1081
1750
|
};
|
|
1082
|
-
`,
|
|
1751
|
+
`,Ie=`export * from "./@api/app";
|
|
1752
|
+
export { appFactory as default } from "./@api/app";
|
|
1083
1753
|
export * from "./@api/dev";
|
|
1084
1754
|
export * from "./@api/errors";
|
|
1085
1755
|
export * from "./@api/router";
|
|
1086
1756
|
export * from "./@api/routes";
|
|
1087
1757
|
export * from "./@api/server";
|
|
1088
|
-
`,
|
|
1089
|
-
import
|
|
1090
|
-
|
|
1091
|
-
import { appFactory, routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1092
|
-
|
|
1093
|
-
export default appFactory(({ createApp }) => {
|
|
1094
|
-
const app = createApp({ router });
|
|
1758
|
+
`,Le=`import appFactory, { routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1759
|
+
import defaultErrorHandler from "./errors";
|
|
1095
1760
|
|
|
1761
|
+
export default appFactory(routes, ({ app }) => {
|
|
1096
1762
|
app.onError(defaultErrorHandler);
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
app.on(methods, [path], ...middleware);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
return app;
|
|
1103
|
-
});
|
|
1104
|
-
`,_e=`import { getRequestListener } from "@hono/node-server";
|
|
1763
|
+
})
|
|
1764
|
+
`,Re=`import { getRequestListener } from "@hono/node-server";
|
|
1105
1765
|
|
|
1106
1766
|
import app from "./app";
|
|
1107
1767
|
|
|
@@ -1115,45 +1775,50 @@ export default devSetup({
|
|
|
1115
1775
|
// close db connections, server sockets etc.
|
|
1116
1776
|
},
|
|
1117
1777
|
});
|
|
1118
|
-
|
|
1778
|
+
|
|
1779
|
+
process.on("unhandledRejection", (reason) => {
|
|
1780
|
+
console.error("💥 UNHANDLED REJECTION");
|
|
1781
|
+
console.error("Reason:", reason);
|
|
1782
|
+
process.exit(1);
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
`,ze=`export declare module "{{ createImport 'libApi' }}" {
|
|
1119
1786
|
interface DefaultVariables {}
|
|
1120
1787
|
interface DefaultBindings {}
|
|
1121
1788
|
}
|
|
1122
|
-
`,
|
|
1789
|
+
`,Be=`import { accepts } from "hono/accepts";
|
|
1123
1790
|
import { HTTPException } from "hono/http-exception";
|
|
1124
1791
|
|
|
1125
1792
|
import { ValidationError, HTTPError } from "@kosmojs/core/errors";
|
|
1126
1793
|
|
|
1127
1794
|
import { errorHandlerFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1128
1795
|
|
|
1129
|
-
export default errorHandlerFactory(
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1796
|
+
export default errorHandlerFactory(async (error, ctx) => {
|
|
1797
|
+
// Let Hono's HTTPException handle its own response
|
|
1798
|
+
if (error instanceof HTTPException) {
|
|
1799
|
+
return error.getResponse();
|
|
1800
|
+
}
|
|
1135
1801
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1802
|
+
const [status, message] = Array.isArray(error)
|
|
1803
|
+
? error
|
|
1804
|
+
: error instanceof HTTPError
|
|
1805
|
+
? [error.status, error.message]
|
|
1806
|
+
: error instanceof ValidationError
|
|
1807
|
+
? [400, \`\${error.target}: \${error.errorMessage}\`]
|
|
1808
|
+
: [error.statusCode || 500, error.message];
|
|
1809
|
+
|
|
1810
|
+
// Respond based on what the client accepts
|
|
1811
|
+
const type = accepts(ctx, {
|
|
1812
|
+
header: "Accept",
|
|
1813
|
+
supports: ["application/json", "text/plain"],
|
|
1814
|
+
default: "text/plain",
|
|
1815
|
+
});
|
|
1150
1816
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
`,be=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
1817
|
+
return type === "application/json"
|
|
1818
|
+
? ctx.json({ error: message }, status)
|
|
1819
|
+
: ctx.text(message, status);
|
|
1820
|
+
});
|
|
1821
|
+
`,Ve=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
1157
1822
|
|
|
1158
1823
|
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
1159
1824
|
GET(async (ctx) => {
|
|
@@ -1162,7 +1827,7 @@ export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
|
1162
1827
|
return ctx.text("Automatically generated route");
|
|
1163
1828
|
}),
|
|
1164
1829
|
]);
|
|
1165
|
-
`,
|
|
1830
|
+
`,He=`import { use } from "{{ createImport 'libApi' }}";
|
|
1166
1831
|
|
|
1167
1832
|
export type UseT = {};
|
|
1168
1833
|
|
|
@@ -1173,20 +1838,11 @@ export default [
|
|
|
1173
1838
|
return next();
|
|
1174
1839
|
}),
|
|
1175
1840
|
];
|
|
1176
|
-
`,
|
|
1177
|
-
|
|
1178
|
-
export default routerFactory(({ createRouter }) => {
|
|
1179
|
-
const router = createRouter();
|
|
1180
|
-
return router;
|
|
1181
|
-
});
|
|
1182
|
-
`,Ce=`import app from "./app";
|
|
1183
|
-
|
|
1184
|
-
import { serverFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1841
|
+
`,Ue=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
1842
|
+
import app from "./app";
|
|
1185
1843
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
});
|
|
1189
|
-
`,we=`import { use } from "{{ createImport 'libApi' }}";
|
|
1844
|
+
await serve(app);
|
|
1845
|
+
`,We=`import { use } from "{{ createImport 'libApi' }}";
|
|
1190
1846
|
|
|
1191
1847
|
/**
|
|
1192
1848
|
* Define global middleware applied to all routes.
|
|
@@ -1197,9 +1853,10 @@ export default [
|
|
|
1197
1853
|
return next();
|
|
1198
1854
|
}),
|
|
1199
1855
|
];
|
|
1200
|
-
`,
|
|
1856
|
+
`,Ge=m((e,n)=>{let{createPath:r,createImportHelpers:i}=_(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=y({helpers:{...i({origin:`lib`}),...S(),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}=y({helpers:i({origin:`src`})}),l=e=>e?.trim().length===0,u=c(n?.templates,Ve),f=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:l}):t===`apiUse`&&await s(r.api(n.file),He,{},{overwrite:l})},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.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=v(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${C(e)}`,alias:d(n),pathTokens:n}]:[]})]}).sort(b);for(let[e,t]of[[`@api/routes.ts`,Ne]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{config({command:e}){return{define:{KOSMO_PRODUCTION_BUILD:e===`build`?`true`:`false`}}},async start(){for(let[e,t]of[[`api.ts`,Fe],[`api:factory.ts`,Ie],[`@api/app.ts`,Oe],[`@api/parsers.ts`,je],[`@api/dev.ts`,ke],[`@api/errors.ts`,Ae],[`@api/router.ts`,Me],[`@api/server.ts`,Pe]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,Le],[`dev.ts`,Re],[`errors.ts`,Be],[`server.ts`,Ue],[`use.ts`,We],[`env.d.ts`,ze]])await s(r.api(e),t,{},{overwrite:l})},async watch(e,t){(!t||t.kind===`create`)&&await f(e),await p(e)},async build(e){await f(e),await p(e)}}}),Ke=p({meta:{name:`Hono`,slot:`backend`},dependencies:{hono:V.devDependencies.hono,"@hono/node-server":V.devDependencies[`@hono/node-server`]},factory:Ge}),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`}},qe=`import Router, { type RouterMiddleware } from "@koa/router";
|
|
1857
|
+
import Koa from "koa";
|
|
1201
1858
|
|
|
1202
|
-
import type {
|
|
1859
|
+
import type { Route, RouteDebugOption } from "@kosmojs/core/api";
|
|
1203
1860
|
|
|
1204
1861
|
import type { DefaultContext, DefaultState } from "../api";
|
|
1205
1862
|
|
|
@@ -1207,34 +1864,86 @@ export type App = Koa<DefaultState, DefaultContext>;
|
|
|
1207
1864
|
|
|
1208
1865
|
export type AppOptions = ConstructorParameters<
|
|
1209
1866
|
typeof Koa<DefaultState, DefaultContext>
|
|
1210
|
-
>[0];
|
|
1867
|
+
>[0] & { router?: Router; debug?: RouteDebugOption };
|
|
1868
|
+
|
|
1869
|
+
export function appFactory(
|
|
1870
|
+
routes: Array<Route<RouterMiddleware>>,
|
|
1871
|
+
options: AppOptions,
|
|
1872
|
+
): App;
|
|
1873
|
+
|
|
1874
|
+
export function appFactory(
|
|
1875
|
+
routes: Array<Route<RouterMiddleware>>,
|
|
1876
|
+
fn: (a: { app: App; router: Router<never> }) => void,
|
|
1877
|
+
): App;
|
|
1878
|
+
|
|
1879
|
+
export function appFactory(
|
|
1880
|
+
routes: Array<Route<RouterMiddleware>>,
|
|
1881
|
+
options: AppOptions,
|
|
1882
|
+
fn: (a: { app: App; router: Router<never> }) => void,
|
|
1883
|
+
): App;
|
|
1884
|
+
|
|
1885
|
+
export function appFactory(
|
|
1886
|
+
routes: Array<Route<RouterMiddleware>>,
|
|
1887
|
+
...rest: Array<unknown>
|
|
1888
|
+
): App {
|
|
1889
|
+
const [options, fn] = typeof rest[0] === "function" ? [{}, rest[0]] : rest;
|
|
1211
1890
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1891
|
+
const {
|
|
1892
|
+
router = new Router(),
|
|
1893
|
+
debug = undefined,
|
|
1894
|
+
...appOptions
|
|
1895
|
+
} = {
|
|
1896
|
+
...(options ? { ...options } : {}),
|
|
1215
1897
|
};
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1898
|
+
|
|
1899
|
+
for (const route of routes) {
|
|
1900
|
+
if (typeof debug === "function") {
|
|
1901
|
+
(debug as Function)(route.debug, route);
|
|
1902
|
+
} else if (debug) {
|
|
1903
|
+
console.log(route.debug[typeof debug === "string" ? debug : "full"]);
|
|
1904
|
+
}
|
|
1905
|
+
router.register(route.path, route.methods, route.middleware, route);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const app = new Koa(appOptions);
|
|
1909
|
+
|
|
1910
|
+
if (typeof fn === "function") {
|
|
1911
|
+
fn({ app, router });
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
return app;
|
|
1915
|
+
}
|
|
1916
|
+
`,Je=`import type { DevSetup } from "@kosmojs/core/api";
|
|
1219
1917
|
|
|
1220
1918
|
export const devSetup = (setup: DevSetup) => setup;
|
|
1221
|
-
`,
|
|
1919
|
+
`,Ye=`import type {
|
|
1920
|
+
DefaultContext,
|
|
1921
|
+
DefaultState,
|
|
1922
|
+
ParameterizedContext,
|
|
1923
|
+
} from "../api";
|
|
1924
|
+
|
|
1925
|
+
type ErrorHandler = (
|
|
1926
|
+
error: any,
|
|
1927
|
+
ctx: ParameterizedContext<unknown, DefaultState, DefaultContext>,
|
|
1928
|
+
) => Promise<void> | void;
|
|
1222
1929
|
|
|
1223
|
-
export type ErrorHandlerFactory = (
|
|
1224
|
-
h: ParameterizedMiddleware,
|
|
1225
|
-
) => ParameterizedMiddleware;
|
|
1930
|
+
export type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;
|
|
1226
1931
|
|
|
1227
1932
|
export const errorHandlerFactory: ErrorHandlerFactory = (handler) => {
|
|
1228
1933
|
return handler;
|
|
1229
1934
|
};
|
|
1230
|
-
`,
|
|
1935
|
+
`,Xe=`import zlib from "node:zlib";
|
|
1231
1936
|
|
|
1232
1937
|
import type { RouterContext } from "@koa/router";
|
|
1233
1938
|
import Formidable, { type Options as FormidableOptions } from "formidable";
|
|
1234
1939
|
import rawParser from "raw-body";
|
|
1235
1940
|
|
|
1236
|
-
import
|
|
1237
|
-
|
|
1941
|
+
import {
|
|
1942
|
+
parseCookies,
|
|
1943
|
+
parseSearchParams,
|
|
1944
|
+
type RequestBodyTarget,
|
|
1945
|
+
type RequestMetadataTarget,
|
|
1946
|
+
} from "@kosmojs/core";
|
|
1238
1947
|
|
|
1239
1948
|
import type {
|
|
1240
1949
|
DefaultContext,
|
|
@@ -1246,7 +1955,7 @@ export const metaparsers: {
|
|
|
1246
1955
|
[T in RequestMetadataTarget]: (ctx: RouterContext) => unknown;
|
|
1247
1956
|
} = {
|
|
1248
1957
|
query(ctx) {
|
|
1249
|
-
return
|
|
1958
|
+
return parseSearchParams(ctx.req.url ?? "");
|
|
1250
1959
|
},
|
|
1251
1960
|
|
|
1252
1961
|
headers(ctx) {
|
|
@@ -1436,8 +2145,7 @@ export const bodyparsers: {
|
|
|
1436
2145
|
return rawParser(stream, rawParserOptions);
|
|
1437
2146
|
},
|
|
1438
2147
|
};
|
|
1439
|
-
`,
|
|
1440
|
-
import { match } from "path-to-regexp";
|
|
2148
|
+
`,Ze=`import type { RouterMiddleware } from "@koa/router";
|
|
1441
2149
|
|
|
1442
2150
|
import type {
|
|
1443
2151
|
RequestBodyTarget,
|
|
@@ -1449,7 +2157,6 @@ import {
|
|
|
1449
2157
|
type CreateRouteMiddleware,
|
|
1450
2158
|
createRoutes,
|
|
1451
2159
|
type HTTPMethod,
|
|
1452
|
-
type RouterFactory,
|
|
1453
2160
|
StateKey,
|
|
1454
2161
|
} from "@kosmojs/core/api";
|
|
1455
2162
|
import { ValidationError } from "@kosmojs/core/errors";
|
|
@@ -1465,10 +2172,6 @@ import { type BodyparserOptions, bodyparsers, metaparsers } from "./parsers";
|
|
|
1465
2172
|
import { routeSources } from "./routes";
|
|
1466
2173
|
|
|
1467
2174
|
import globalMiddleware from "{{ createImport 'api' 'use' }}";
|
|
1468
|
-
import { apiRouteMap } from "{{ createImport 'libCore' }}";
|
|
1469
|
-
|
|
1470
|
-
export type Router = import("@koa/router").Router<DefaultState, DefaultContext>;
|
|
1471
|
-
export type RouterOptions = import("@koa/router").RouterOptions;
|
|
1472
2175
|
|
|
1473
2176
|
/**
|
|
1474
2177
|
* Create route-level middleware stack that handles:
|
|
@@ -1485,33 +2188,7 @@ export type RouterOptions = import("@koa/router").RouterOptions;
|
|
|
1485
2188
|
* */
|
|
1486
2189
|
export const createRouteMiddleware: CreateRouteMiddleware<
|
|
1487
2190
|
ParameterizedMiddleware
|
|
1488
|
-
> = ({ name,
|
|
1489
|
-
const route = apiRouteMap[name];
|
|
1490
|
-
|
|
1491
|
-
if (!route) {
|
|
1492
|
-
throw new Error(\`createRouteMiddleware: \${name} route does not exists\`);
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
const { params, numericProperties } = route;
|
|
1496
|
-
|
|
1497
|
-
const pathMatcher = match(pathPattern);
|
|
1498
|
-
|
|
1499
|
-
const matchPath = (path: string) => {
|
|
1500
|
-
try {
|
|
1501
|
-
return pathMatcher(path);
|
|
1502
|
-
} catch (e) {
|
|
1503
|
-
return undefined;
|
|
1504
|
-
}
|
|
1505
|
-
};
|
|
1506
|
-
|
|
1507
|
-
const maybeNumber = (val: unknown) => {
|
|
1508
|
-
if (val === undefined || val === null) {
|
|
1509
|
-
return val;
|
|
1510
|
-
}
|
|
1511
|
-
const n = Number(val);
|
|
1512
|
-
return Number.isFinite(n) ? n : val;
|
|
1513
|
-
};
|
|
1514
|
-
|
|
2191
|
+
> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {
|
|
1515
2192
|
const validationMiddleware = [
|
|
1516
2193
|
/**
|
|
1517
2194
|
* Extends Koa context with:
|
|
@@ -1552,16 +2229,7 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1552
2229
|
ctx[StateKey].set(
|
|
1553
2230
|
target,
|
|
1554
2231
|
target === "query"
|
|
1555
|
-
?
|
|
1556
|
-
Object.entries(parser(ctx)).map(([k, v]) => [
|
|
1557
|
-
k,
|
|
1558
|
-
numericProperties.query[ctx.method]?.includes(k)
|
|
1559
|
-
? Array.isArray(v)
|
|
1560
|
-
? v.map((e) => maybeNumber(e))
|
|
1561
|
-
: maybeNumber(v)
|
|
1562
|
-
: v,
|
|
1563
|
-
]),
|
|
1564
|
-
)
|
|
2232
|
+
? normalizeSearchParams(parser(ctx), ctx.method as never)
|
|
1565
2233
|
: parser(ctx),
|
|
1566
2234
|
);
|
|
1567
2235
|
}
|
|
@@ -1613,23 +2281,7 @@ export const createRouteMiddleware: CreateRouteMiddleware<
|
|
|
1613
2281
|
* */
|
|
1614
2282
|
use(
|
|
1615
2283
|
function useValidateParams(ctx, next) {
|
|
1616
|
-
const
|
|
1617
|
-
const normalizedParams = params.reduce(
|
|
1618
|
-
(map: Record<string, unknown>, name) => {
|
|
1619
|
-
const value = matched ? matched.params[name] : undefined;
|
|
1620
|
-
if (Array.isArray(value)) {
|
|
1621
|
-
map[name] = numericProperties.params.includes(name)
|
|
1622
|
-
? value.map((e) => maybeNumber(e))
|
|
1623
|
-
: value;
|
|
1624
|
-
} else if (value) {
|
|
1625
|
-
map[name] = numericProperties.params.includes(name)
|
|
1626
|
-
? maybeNumber(value)
|
|
1627
|
-
: value;
|
|
1628
|
-
}
|
|
1629
|
-
return map;
|
|
1630
|
-
},
|
|
1631
|
-
{},
|
|
1632
|
-
);
|
|
2284
|
+
const normalizedParams = normalizeParams(ctx.path);
|
|
1633
2285
|
validationSchemas.params?.validate(normalizedParams);
|
|
1634
2286
|
ctx[StateKey].set("params", normalizedParams);
|
|
1635
2287
|
return next();
|
|
@@ -1858,32 +2510,21 @@ export const routes = createRoutes<ParameterizedMiddleware, RouterMiddleware>(
|
|
|
1858
2510
|
createRouteMiddleware,
|
|
1859
2511
|
},
|
|
1860
2512
|
);
|
|
1861
|
-
|
|
1862
|
-
export const routerFactory: RouterFactory<Router, RouterOptions> = (
|
|
1863
|
-
factory,
|
|
1864
|
-
) => {
|
|
1865
|
-
const createRouter = (options?: RouterOptions): Router => {
|
|
1866
|
-
return new KoaRouter(options);
|
|
1867
|
-
};
|
|
1868
|
-
return factory({ createRouter });
|
|
1869
|
-
};
|
|
1870
|
-
`,Me=`import { join } from "node:path";
|
|
2513
|
+
`,Qe=`import { join } from "node:path";
|
|
1871
2514
|
|
|
1872
2515
|
import type { RouteSource } from "@kosmojs/core/api";
|
|
1873
2516
|
|
|
1874
|
-
import { base, apiBase } from "{{ createImport 'libCore' }}";
|
|
2517
|
+
import { base, apiBase, apiRouteMap, apiRouteMapper } from "{{ createImport 'libCore' }}";
|
|
1875
2518
|
|
|
1876
2519
|
{{#each routes}}
|
|
1877
2520
|
import {{id}} from "{{ createImport 'api' file }}";
|
|
1878
|
-
import { validationSchemas as {{id}}_schemas } from "{{ createImport 'libApi'
|
|
2521
|
+
import { validationSchemas as {{id}}_schemas } from "{{ createImport 'libApi' basename 'schemas' }}";
|
|
1879
2522
|
{{/each}}
|
|
1880
2523
|
|
|
1881
2524
|
{{#each cascadingMiddleware}}
|
|
1882
2525
|
import {{id}}, { type UseT as UseT{{id}} } from "{{ createImport 'api' file }}";
|
|
1883
2526
|
{{/each}}
|
|
1884
2527
|
|
|
1885
|
-
type Override<A, B> = Omit<A, keyof B> & B;
|
|
1886
|
-
|
|
1887
2528
|
export type RouteMap = {
|
|
1888
2529
|
{{#each routes}}
|
|
1889
2530
|
"{{name}}": {
|
|
@@ -1897,65 +2538,63 @@ export type RouteMap = {
|
|
|
1897
2538
|
export const routeSources: Array<RouteSource<never>> = [
|
|
1898
2539
|
{{#each routes}}
|
|
1899
2540
|
{
|
|
1900
|
-
|
|
1901
|
-
{{
|
|
1902
|
-
path: "{{
|
|
1903
|
-
pathPattern: "{{
|
|
2541
|
+
{{#if alias}}
|
|
2542
|
+
...apiRouteMapper(apiBase, { ...{{serializeApiRoute .}}, pathPattern: "{{alias}}" }),
|
|
2543
|
+
path: "{{alias}}",
|
|
2544
|
+
pathPattern: "{{alias}}",
|
|
1904
2545
|
{{else}}
|
|
2546
|
+
...apiRouteMap["{{name}}"],
|
|
1905
2547
|
path: join(base, apiBase, "{{path}}"),
|
|
1906
2548
|
pathPattern: join(base, apiBase, "{{pathPattern}}"),
|
|
1907
2549
|
{{/if}}
|
|
2550
|
+
name: "{{name}}",
|
|
1908
2551
|
file: "{{file}}",
|
|
1909
2552
|
cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,
|
|
1910
2553
|
definitionItems: {{id}} as never,
|
|
1911
2554
|
validationSchemas: {{id}}_schemas,
|
|
1912
2555
|
},
|
|
1913
2556
|
{{/each}}
|
|
1914
|
-
]
|
|
1915
|
-
|
|
2557
|
+
];
|
|
2558
|
+
`,$e=`import { chmod, unlink } from "node:fs/promises";
|
|
1916
2559
|
import { parseArgs, styleText } from "node:util";
|
|
1917
2560
|
|
|
1918
|
-
import type { ServerFactory } from "@kosmojs/core/api";
|
|
1919
|
-
|
|
1920
2561
|
import type { App } from "./app";
|
|
1921
2562
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
short: "p",
|
|
1928
|
-
},
|
|
1929
|
-
sock: {
|
|
1930
|
-
type: "string",
|
|
1931
|
-
short: "s",
|
|
1932
|
-
},
|
|
1933
|
-
},
|
|
1934
|
-
});
|
|
1935
|
-
|
|
1936
|
-
const getListenHandles = async () => {
|
|
1937
|
-
const { port, sock } = { ...values };
|
|
2563
|
+
type Handles = {
|
|
2564
|
+
port?: number | undefined;
|
|
2565
|
+
sock?: string | undefined;
|
|
2566
|
+
onListen?: () => Promise<void>;
|
|
2567
|
+
};
|
|
1938
2568
|
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2569
|
+
const getListenHandles = async (opt?: Handles): Promise<Handles> => {
|
|
2570
|
+
const { port, sock } = opt
|
|
2571
|
+
? opt
|
|
2572
|
+
: parseArgs({
|
|
2573
|
+
options: {
|
|
2574
|
+
port: {
|
|
2575
|
+
type: "string",
|
|
2576
|
+
short: "p",
|
|
2577
|
+
},
|
|
2578
|
+
sock: {
|
|
2579
|
+
type: "string",
|
|
2580
|
+
short: "s",
|
|
2581
|
+
},
|
|
2582
|
+
},
|
|
2583
|
+
}).values;
|
|
1943
2584
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
return;
|
|
1948
|
-
}
|
|
1949
|
-
console.error(error.message);
|
|
1950
|
-
process.exit(1);
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
2585
|
+
if (![port, sock].some(Boolean)) {
|
|
2586
|
+
throw new Error("Please provide either -p/--port number or -s/--sock path");
|
|
2587
|
+
}
|
|
1953
2588
|
|
|
1954
|
-
|
|
1955
|
-
|
|
2589
|
+
if (sock) {
|
|
2590
|
+
await unlink(sock).catch((error) => {
|
|
2591
|
+
if (error.code !== "ENOENT") {
|
|
2592
|
+
throw error;
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
1956
2596
|
|
|
1957
2597
|
const onListen = async () => {
|
|
1958
|
-
const { port, sock } = await getListenHandles();
|
|
1959
2598
|
if (sock) {
|
|
1960
2599
|
// Make Unix socket world-writable so other processes (e.g. a reverse proxy)
|
|
1961
2600
|
// can connect without permission issues.
|
|
@@ -1967,30 +2606,19 @@ export const serverFactory: ServerFactory<App> = (factory) => {
|
|
|
1967
2606
|
);
|
|
1968
2607
|
};
|
|
1969
2608
|
|
|
1970
|
-
return
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
},
|
|
1976
|
-
getListenHandles,
|
|
1977
|
-
onListen,
|
|
1978
|
-
});
|
|
2609
|
+
return {
|
|
2610
|
+
port: port ? Number(port) : undefined,
|
|
2611
|
+
sock,
|
|
2612
|
+
onListen: opt?.onListen || onListen,
|
|
2613
|
+
};
|
|
1979
2614
|
};
|
|
1980
2615
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
console.error("");
|
|
1988
|
-
// In development, crash hard
|
|
1989
|
-
if (process.env.NODE_ENV === "development") {
|
|
1990
|
-
process.exit(1);
|
|
1991
|
-
}
|
|
1992
|
-
});
|
|
1993
|
-
`,Pe=`import type { RouterContext } from "@koa/router";
|
|
2616
|
+
export const serve = async <T extends App>(app: T, opt?: Handles) => {
|
|
2617
|
+
const { port, sock, onListen } = await getListenHandles(opt);
|
|
2618
|
+
const server = app.listen(port || sock, onListen);
|
|
2619
|
+
return server as never;
|
|
2620
|
+
};
|
|
2621
|
+
`,et=`import type { RouterContext } from "@koa/router";
|
|
1994
2622
|
import type { Next } from "koa";
|
|
1995
2623
|
|
|
1996
2624
|
import type { ValidationDefmap, ValidationOptmap } from "@kosmojs/core";
|
|
@@ -2152,25 +2780,25 @@ export const defineRoute: <
|
|
|
2152
2780
|
use: use as never,
|
|
2153
2781
|
});
|
|
2154
2782
|
};
|
|
2155
|
-
`,
|
|
2783
|
+
`,tt=`export * from "./@api/app";
|
|
2784
|
+
export { appFactory as default } from "./@api/app";
|
|
2156
2785
|
export * from "./@api/dev";
|
|
2157
2786
|
export * from "./@api/errors";
|
|
2158
2787
|
export * from "./@api/router";
|
|
2159
2788
|
export * from "./@api/routes";
|
|
2160
2789
|
export * from "./@api/server";
|
|
2161
|
-
`,
|
|
2790
|
+
`,nt=`import appFactory, { routes } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2791
|
+
import defaultErrorHandler from "./errors";
|
|
2162
2792
|
|
|
2163
|
-
|
|
2793
|
+
export default appFactory(routes, ({ app, router }) => {
|
|
2164
2794
|
|
|
2165
|
-
|
|
2166
|
-
const app = createApp();
|
|
2795
|
+
app.on("error", defaultErrorHandler);
|
|
2167
2796
|
|
|
2168
2797
|
// NOTE: Routes should be added last, after any middleware
|
|
2169
2798
|
app.use(router.routes());
|
|
2170
2799
|
|
|
2171
|
-
return app;
|
|
2172
2800
|
});
|
|
2173
|
-
`,
|
|
2801
|
+
`,rt=`import app from "./app";
|
|
2174
2802
|
|
|
2175
2803
|
import { devSetup } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2176
2804
|
|
|
@@ -2182,45 +2810,45 @@ export default devSetup({
|
|
|
2182
2810
|
// close db connections, server sockets etc.
|
|
2183
2811
|
},
|
|
2184
2812
|
});
|
|
2185
|
-
|
|
2813
|
+
|
|
2814
|
+
process.on("unhandledRejection", (reason) => {
|
|
2815
|
+
console.error("💥 UNHANDLED REJECTION");
|
|
2816
|
+
console.error("Reason:", reason);
|
|
2817
|
+
process.exit(1);
|
|
2818
|
+
});
|
|
2819
|
+
`,it=`export declare module "{{ createImport 'libApi' }}" {
|
|
2186
2820
|
interface DefaultState {}
|
|
2187
2821
|
interface DefaultContext {}
|
|
2188
2822
|
}
|
|
2189
|
-
`,
|
|
2823
|
+
`,at=`import { HTTPError, ValidationError } from "@kosmojs/core/errors";
|
|
2190
2824
|
|
|
2191
2825
|
import { errorHandlerFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2192
2826
|
|
|
2193
|
-
export default errorHandlerFactory(
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
? error
|
|
2200
|
-
: error
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
ctx.body = message;
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
},
|
|
2215
|
-
);
|
|
2216
|
-
`,Be=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
2827
|
+
export default errorHandlerFactory(async (error, ctx) => {
|
|
2828
|
+
const [status, message] = Array.isArray(error)
|
|
2829
|
+
? error
|
|
2830
|
+
: error instanceof HTTPError
|
|
2831
|
+
? [error.status, error.message]
|
|
2832
|
+
: error instanceof ValidationError
|
|
2833
|
+
? [400, \`\${error.target}: \${error.errorMessage}\`]
|
|
2834
|
+
: [error.statusCode || 500, error.message];
|
|
2835
|
+
|
|
2836
|
+
ctx.status = status;
|
|
2837
|
+
|
|
2838
|
+
if (ctx.accepts("json")) {
|
|
2839
|
+
ctx.body = { error: message };
|
|
2840
|
+
} else {
|
|
2841
|
+
ctx.body = message;
|
|
2842
|
+
}
|
|
2843
|
+
});
|
|
2844
|
+
`,ot=`import { defineRoute } from "{{ createImport 'libApi' }}";
|
|
2217
2845
|
|
|
2218
2846
|
export default defineRoute<"{{route.name}}">(({ GET }) => [
|
|
2219
2847
|
GET(async (ctx) => {
|
|
2220
2848
|
ctx.body = "Automatically generated route";
|
|
2221
2849
|
}),
|
|
2222
2850
|
]);
|
|
2223
|
-
`,
|
|
2851
|
+
`,st=`import { use } from "{{ createImport 'libApi' }}";
|
|
2224
2852
|
|
|
2225
2853
|
export type UseT = {};
|
|
2226
2854
|
|
|
@@ -2231,46 +2859,32 @@ export default [
|
|
|
2231
2859
|
return next();
|
|
2232
2860
|
}),
|
|
2233
2861
|
];
|
|
2234
|
-
`,
|
|
2235
|
-
|
|
2236
|
-
export default routerFactory(({ createRouter }) => {
|
|
2237
|
-
const router = createRouter();
|
|
2238
|
-
|
|
2239
|
-
for (const { name, path, methods, middleware } of routes) {
|
|
2240
|
-
router.register(path, methods, middleware, { name });
|
|
2241
|
-
}
|
|
2242
|
-
|
|
2243
|
-
return router;
|
|
2244
|
-
});
|
|
2245
|
-
`,Ue=`import app from "./app";
|
|
2246
|
-
|
|
2247
|
-
import { serverFactory } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2248
|
-
|
|
2249
|
-
serverFactory(async ({ createServer }) => {
|
|
2250
|
-
await createServer(app);
|
|
2251
|
-
});
|
|
2252
|
-
`,We=`import defaultErrorHandler from "./errors";
|
|
2862
|
+
`,ct=`import { serve } from "{{ createImport 'lib' 'api:factory' }}";
|
|
2863
|
+
import app from "./app";
|
|
2253
2864
|
|
|
2254
|
-
|
|
2865
|
+
await serve(app);
|
|
2866
|
+
`,lt=`import { use } from "{{ createImport 'libApi' }}";
|
|
2255
2867
|
|
|
2868
|
+
/**
|
|
2869
|
+
* Define global middleware applied to all routes.
|
|
2870
|
+
* Can be overridden on a per-route basis using the slot key.
|
|
2871
|
+
* */
|
|
2256
2872
|
export default [
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
* */
|
|
2261
|
-
use(defaultErrorHandler, { slot: "errorHandler" }),
|
|
2873
|
+
use(async function useExample(ctx, next) {
|
|
2874
|
+
return next();
|
|
2875
|
+
}),
|
|
2262
2876
|
];
|
|
2263
|
-
`,
|
|
2877
|
+
`,ut=m((e,n)=>{let{createPath:r,createImportHelpers:i}=_(e),a=e=>e.length===0?`{}`:e.length===1?e[0]:`Override<${e[0]}, ${a(e.slice(1))}>`,{renderToFile:o}=y({helpers:{...i({origin:`lib`}),...S(),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}=y({helpers:i({origin:`src`})}),l=e=>e?.trim().length===0,u=c(n?.templates,ot),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:l}):t===`apiUse`&&await s(r.api(n.file),st,{},{overwrite:l})},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,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=v(e);return t===r.name?[{...o,name:e,basename:r.name,id:`${o.id}_${C(e)}`,alias:f(n),pathTokens:n}]:[]})]}).sort(b);for(let[e,t]of[[`@api/routes.ts`,Qe]])await o(r.lib(e),t,{routes:a,cascadingMiddleware:i})};return{config({command:e}){return{define:{KOSMO_PRODUCTION_BUILD:e===`build`?`true`:`false`}}},async start(){for(let[e,t]of[[`api.ts`,et],[`api:factory.ts`,tt],[`@api/app.ts`,qe],[`@api/dev.ts`,Je],[`@api/errors.ts`,Ye],[`@api/parsers.ts`,Xe],[`@api/router.ts`,Ze],[`@api/server.ts`,$e]])await o(r.lib(e),t,{});for(let[e,t]of[[`app.ts`,nt],[`dev.ts`,rt],[`errors.ts`,at],[`server.ts`,ct],[`use.ts`,lt],[`env.d.ts`,it]])await s(r.api(e),t,{},{overwrite:l})},async watch(e,t){(!t||t.kind===`create`)&&await d(e),await p(e)},async build(e){await d(e),await p(e)}}}),dt=p({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:ut}),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`}},ft=()=>{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)]},pt=(e,t,n)=>{let{remarkPlugins:r=[],rehypePlugins:i=[]}={...n},a=()=>{let t=[`${l.srcDir}/${e.name}/${l.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,`
|
|
2264
2878
|
if (import.meta.hot) {
|
|
2265
2879
|
import.meta.hot.accept(() => {});
|
|
2266
2880
|
}
|
|
2267
2881
|
`].join(`
|
|
2268
|
-
`)}}}},o=[ne({jsxImportSource:`preact`,providerImportSource:`@mdx-js/preact`,remarkPlugins:r,rehypePlugins:i})];return t===`serve`&&o.push(a()),o},
|
|
2882
|
+
`)}}}},o=[ne({jsxImportSource:`preact`,providerImportSource:`@mdx-js/preact`,remarkPlugins:r,rehypePlugins:i})];return t===`serve`&&o.push(a()),o},mt=`import type { FunctionComponent } from "preact";
|
|
2269
2883
|
|
|
2270
2884
|
export const AppProvider: FunctionComponent = (props) => {
|
|
2271
2885
|
return props.children;
|
|
2272
2886
|
};
|
|
2273
|
-
`,
|
|
2887
|
+
`,ht=`import { render, hydrate as hydrateOrig } from "preact";
|
|
2274
2888
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
2275
2889
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
2276
2890
|
|
|
@@ -2311,7 +2925,7 @@ export const mount = async (
|
|
|
2311
2925
|
}
|
|
2312
2926
|
|
|
2313
2927
|
export default clientRenderFactory();
|
|
2314
|
-
`,
|
|
2928
|
+
`,gt=`import { renderToString as renderToStringOrig } from "preact-render-to-string";
|
|
2315
2929
|
|
|
2316
2930
|
import type {
|
|
2317
2931
|
RenderToStringWrapper,
|
|
@@ -2385,7 +2999,7 @@ export const renderToString: RenderToStringWrapper<
|
|
|
2385
2999
|
}
|
|
2386
3000
|
|
|
2387
3001
|
export default serverRenderFactory<false>();
|
|
2388
|
-
`,
|
|
3002
|
+
`,_t=`declare module "*.mdx" {
|
|
2389
3003
|
import type { ComponentType } from "preact";
|
|
2390
3004
|
export const frontmatter: Record<string, unknown>;
|
|
2391
3005
|
const component: ComponentType;
|
|
@@ -2398,10 +3012,12 @@ declare module "*.md" {
|
|
|
2398
3012
|
const component: ComponentType;
|
|
2399
3013
|
export default component;
|
|
2400
3014
|
}
|
|
2401
|
-
|
|
3015
|
+
`,vt=`import { MDXProvider } from "@mdx-js/preact";
|
|
2402
3016
|
import { match, pathToRegexp } from "path-to-regexp";
|
|
2403
3017
|
import { type ComponentType, createContext, h, type VNode } from "preact";
|
|
2404
3018
|
|
|
3019
|
+
import { parseSearchParams } from "@kosmojs/core";
|
|
3020
|
+
|
|
2405
3021
|
import { paramNames } from "{{ createImport 'lib' 'params' }}";
|
|
2406
3022
|
import { base } from "{{ createImport 'libCore' }}";
|
|
2407
3023
|
|
|
@@ -2415,7 +3031,7 @@ export type RawRoute = {
|
|
|
2415
3031
|
};
|
|
2416
3032
|
|
|
2417
3033
|
type Loader = (
|
|
2418
|
-
route: Pick<Route, "name" | "params" | "paramsEntries">,
|
|
3034
|
+
route: Pick<Route, "name" | "params" | "paramsEntries" | "searchParams">,
|
|
2419
3035
|
) => Promise<unknown> | undefined;
|
|
2420
3036
|
|
|
2421
3037
|
type LayoutModule = {
|
|
@@ -2435,6 +3051,7 @@ export type Route = {
|
|
|
2435
3051
|
name: string;
|
|
2436
3052
|
params: Record<string, string | Array<string>>;
|
|
2437
3053
|
paramsEntries: [keys: Array<string>, values: Array<unknown>];
|
|
3054
|
+
searchParams: Record<string, unknown>;
|
|
2438
3055
|
frontmatter: Record<string, unknown>;
|
|
2439
3056
|
loaderData: Record<string, unknown>;
|
|
2440
3057
|
};
|
|
@@ -2443,6 +3060,7 @@ export const RouterContext = createContext<Route>({
|
|
|
2443
3060
|
name: "",
|
|
2444
3061
|
params: {},
|
|
2445
3062
|
paramsEntries: [[], []],
|
|
3063
|
+
searchParams: {},
|
|
2446
3064
|
frontmatter: {},
|
|
2447
3065
|
loaderData: {},
|
|
2448
3066
|
});
|
|
@@ -2472,6 +3090,7 @@ export const createRouter = (
|
|
|
2472
3090
|
|
|
2473
3091
|
return {
|
|
2474
3092
|
async resolve(url: URL = new URL(window.location.href)) {
|
|
3093
|
+
const searchParams = parseSearchParams(url);
|
|
2475
3094
|
const urlSegments = url.pathname.split("/").filter(Boolean).length;
|
|
2476
3095
|
|
|
2477
3096
|
// 1: use lightweight \`RegExp.test()\` on linear scan - no capture allocation
|
|
@@ -2516,7 +3135,7 @@ export const createRouter = (
|
|
|
2516
3135
|
|
|
2517
3136
|
loaderData[name] = await runLoader(name, () => {
|
|
2518
3137
|
return routeModule.loader
|
|
2519
|
-
? routeModule.loader({ name, params, paramsEntries })
|
|
3138
|
+
? routeModule.loader({ name, params, paramsEntries, searchParams })
|
|
2520
3139
|
: undefined; // should not survive serialization if no loader defined
|
|
2521
3140
|
});
|
|
2522
3141
|
|
|
@@ -2531,7 +3150,7 @@ export const createRouter = (
|
|
|
2531
3150
|
const key = \`\${name}/layout\`;
|
|
2532
3151
|
loaderData[key] = await runLoader(key, () => {
|
|
2533
3152
|
return layoutModule.loader
|
|
2534
|
-
? layoutModule.loader({ name, params, paramsEntries })
|
|
3153
|
+
? layoutModule.loader({ name, params, paramsEntries, searchParams })
|
|
2535
3154
|
: undefined; // should not survive serialization if no loader defined
|
|
2536
3155
|
});
|
|
2537
3156
|
}
|
|
@@ -2540,6 +3159,7 @@ export const createRouter = (
|
|
|
2540
3159
|
name,
|
|
2541
3160
|
params,
|
|
2542
3161
|
paramsEntries,
|
|
3162
|
+
searchParams,
|
|
2543
3163
|
frontmatter,
|
|
2544
3164
|
loaderData,
|
|
2545
3165
|
};
|
|
@@ -2588,7 +3208,7 @@ export const createRoute = (
|
|
|
2588
3208
|
layouts,
|
|
2589
3209
|
};
|
|
2590
3210
|
};
|
|
2591
|
-
`,
|
|
3211
|
+
`,yt=`/* @jsxImportSource preact */
|
|
2592
3212
|
|
|
2593
3213
|
import styles from "./styles.module.css";
|
|
2594
3214
|
|
|
@@ -2629,7 +3249,7 @@ export default function PageSample(props: {
|
|
|
2629
3249
|
</div>
|
|
2630
3250
|
);
|
|
2631
3251
|
}
|
|
2632
|
-
`,
|
|
3252
|
+
`,bt=`/* @jsxImportSource preact */
|
|
2633
3253
|
|
|
2634
3254
|
import styles from "./styles.module.css";
|
|
2635
3255
|
|
|
@@ -2681,7 +3301,7 @@ export default function PageSample(props: {
|
|
|
2681
3301
|
</div>
|
|
2682
3302
|
);
|
|
2683
3303
|
}
|
|
2684
|
-
`,
|
|
3304
|
+
`,xt=`* {
|
|
2685
3305
|
margin: 0;
|
|
2686
3306
|
padding: 0;
|
|
2687
3307
|
box-sizing: border-box;
|
|
@@ -2816,7 +3436,7 @@ export default function PageSample(props: {
|
|
|
2816
3436
|
align-items: center;
|
|
2817
3437
|
gap: 0.25rem;
|
|
2818
3438
|
}
|
|
2819
|
-
`,
|
|
3439
|
+
`,St=`/* @jsxImportSource preact */
|
|
2820
3440
|
|
|
2821
3441
|
import styles from "./styles.module.css";
|
|
2822
3442
|
|
|
@@ -2882,7 +3502,7 @@ export default function WelcomePage() {
|
|
|
2882
3502
|
</div>
|
|
2883
3503
|
);
|
|
2884
3504
|
}
|
|
2885
|
-
`,
|
|
3505
|
+
`,Ct=`export type ParamsMap = {
|
|
2886
3506
|
{{#each pageRoutes}}"{{name}}": {{serializeParamsLiteral .}};
|
|
2887
3507
|
{{/each}}
|
|
2888
3508
|
};
|
|
@@ -2891,7 +3511,7 @@ export const paramNames = {
|
|
|
2891
3511
|
{{#each pageRoutes}}"{{name}}": [ {{#each params.schema}}"{{name}}", {{/each}}],
|
|
2892
3512
|
{{/each}}
|
|
2893
3513
|
} as const;
|
|
2894
|
-
`,
|
|
3514
|
+
`,wt=`import type { ComponentType } from "preact";
|
|
2895
3515
|
|
|
2896
3516
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
2897
3517
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
@@ -2935,7 +3555,7 @@ export default createRouterFactory<
|
|
|
2935
3555
|
Promise<RouteComponent>,
|
|
2936
3556
|
{ server: { route: Route } }
|
|
2937
3557
|
>();
|
|
2938
|
-
`,
|
|
3558
|
+
`,Tt=`import { join } from "node:path";
|
|
2939
3559
|
|
|
2940
3560
|
import { compile } from "path-to-regexp";
|
|
2941
3561
|
|
|
@@ -2981,7 +3601,7 @@ export default Object.entries(routes)
|
|
|
2981
3601
|
return [];
|
|
2982
3602
|
})
|
|
2983
3603
|
.map((path) => join(base, path));
|
|
2984
|
-
`,
|
|
3604
|
+
`,Et=`import type { PageRoute } from "@kosmojs/core";
|
|
2985
3605
|
|
|
2986
3606
|
{{#each pageRoutes}}
|
|
2987
3607
|
import * as {{id}} from "{{ createImport 'pages' file }}";
|
|
@@ -3005,18 +3625,18 @@ const routeMap: Record<
|
|
|
3005
3625
|
}
|
|
3006
3626
|
|
|
3007
3627
|
export default routeMap;
|
|
3008
|
-
`,
|
|
3628
|
+
`,Dt=`import { useContext } from "preact/hooks";
|
|
3009
3629
|
|
|
3010
3630
|
import { RouterContext } from "./mdx";
|
|
3011
3631
|
|
|
3012
3632
|
import type { ParamsMap, paramNames } from "{{ createImport 'lib' 'params' }}";
|
|
3013
3633
|
|
|
3014
3634
|
export function useRoute() {
|
|
3015
|
-
return useContext(RouterContext);
|
|
3635
|
+
return structuredClone(useContext(RouterContext));
|
|
3016
3636
|
}
|
|
3017
3637
|
|
|
3018
3638
|
export function useParams<T extends keyof ParamsMap>(): ParamsMap[T] {
|
|
3019
|
-
return
|
|
3639
|
+
return useRoute().params as ParamsMap[T];
|
|
3020
3640
|
}
|
|
3021
3641
|
|
|
3022
3642
|
type SameLengthTuple<T extends readonly unknown[], U> = { [K in keyof T]: U };
|
|
@@ -3025,7 +3645,11 @@ export function useParamsEntries<T extends keyof ParamsMap>(): [
|
|
|
3025
3645
|
(typeof paramNames)[T],
|
|
3026
3646
|
SameLengthTuple<(typeof paramNames)[T], unknown>,
|
|
3027
3647
|
] {
|
|
3028
|
-
return
|
|
3648
|
+
return useRoute().paramsEntries as never;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
export function useSearchParams() {
|
|
3652
|
+
return useRoute().searchParams;
|
|
3029
3653
|
}
|
|
3030
3654
|
|
|
3031
3655
|
/**
|
|
@@ -3035,7 +3659,7 @@ export function useParamsEntries<T extends keyof ParamsMap>(): [
|
|
|
3035
3659
|
* a hook can't tell which layout it runs in.
|
|
3036
3660
|
* */
|
|
3037
3661
|
export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
3038
|
-
const route =
|
|
3662
|
+
const route = useRoute();
|
|
3039
3663
|
return route.loaderData?.[key || route.name] as T;
|
|
3040
3664
|
};
|
|
3041
3665
|
|
|
@@ -3045,12 +3669,12 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
3045
3669
|
export const useFrontmatter = <
|
|
3046
3670
|
T extends Record<string, unknown> = Record<string, unknown>,
|
|
3047
3671
|
>(): T => {
|
|
3048
|
-
return
|
|
3672
|
+
return useRoute().frontmatter as T;
|
|
3049
3673
|
};
|
|
3050
|
-
`,
|
|
3674
|
+
`,Ot=`import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
3051
3675
|
|
|
3052
3676
|
<AppProvider>{props.children}</AppProvider>
|
|
3053
|
-
`,
|
|
3677
|
+
`,kt=`import { h, type JSX } from "preact";
|
|
3054
3678
|
|
|
3055
3679
|
import { pageRouteMap, type LinkProps } from "{{ createImport 'libCore' }}";
|
|
3056
3680
|
|
|
@@ -3067,7 +3691,7 @@ export default function Link(
|
|
|
3067
3691
|
|
|
3068
3692
|
return h("a", { ...restProps, href }, children);
|
|
3069
3693
|
}
|
|
3070
|
-
`,
|
|
3694
|
+
`,At=`/**
|
|
3071
3695
|
* MDX component overrides.
|
|
3072
3696
|
*
|
|
3073
3697
|
* Every standard markdown element (headings, links, code blocks, etc.)
|
|
@@ -3090,7 +3714,7 @@ export const components = {
|
|
|
3090
3714
|
declare global {
|
|
3091
3715
|
type MDXProvidedComponents = typeof components;
|
|
3092
3716
|
}
|
|
3093
|
-
`,
|
|
3717
|
+
`,jt=`import renderFactory, {
|
|
3094
3718
|
createRoutes,
|
|
3095
3719
|
hydrate,
|
|
3096
3720
|
mount,
|
|
@@ -3117,7 +3741,7 @@ if (root) {
|
|
|
3117
3741
|
} else {
|
|
3118
3742
|
console.error("❌ Root element not found!");
|
|
3119
3743
|
}
|
|
3120
|
-
`,
|
|
3744
|
+
`,Mt=`import renderFactory, {
|
|
3121
3745
|
createRoutes,
|
|
3122
3746
|
renderToString,
|
|
3123
3747
|
// no renderToStream on MDX folders
|
|
@@ -3138,7 +3762,7 @@ export default renderFactory(() => {
|
|
|
3138
3762
|
},
|
|
3139
3763
|
};
|
|
3140
3764
|
});
|
|
3141
|
-
`,
|
|
3765
|
+
`,Nt=`<!doctype html>
|
|
3142
3766
|
<html lang="en">
|
|
3143
3767
|
<head>
|
|
3144
3768
|
<meta charset="UTF-8" />
|
|
@@ -3150,13 +3774,13 @@ export default renderFactory(() => {
|
|
|
3150
3774
|
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
3151
3775
|
</body>
|
|
3152
3776
|
</html>
|
|
3153
|
-
`,
|
|
3777
|
+
`,Pt=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
3154
3778
|
|
|
3155
3779
|
export default function Page() {
|
|
3156
3780
|
return <PageSample />;
|
|
3157
3781
|
}
|
|
3158
|
-
`,
|
|
3159
|
-
`,
|
|
3782
|
+
`,Ft=`{props.children}
|
|
3783
|
+
`,It=`---
|
|
3160
3784
|
title: "{{title}}"
|
|
3161
3785
|
---
|
|
3162
3786
|
|
|
@@ -3173,7 +3797,7 @@ export const pathMap = {
|
|
|
3173
3797
|
routeName="{{route.name}}"
|
|
3174
3798
|
pathMap={pathMap}
|
|
3175
3799
|
/>
|
|
3176
|
-
`,
|
|
3800
|
+
`,Lt=`---
|
|
3177
3801
|
title: Welcome to KosmoJS
|
|
3178
3802
|
description: Content-first development with MDX and Vite
|
|
3179
3803
|
---
|
|
@@ -3181,7 +3805,7 @@ description: Content-first development with MDX and Vite
|
|
|
3181
3805
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}"
|
|
3182
3806
|
|
|
3183
3807
|
<WelcomePage />
|
|
3184
|
-
`,
|
|
3808
|
+
`,Rt=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
3185
3809
|
|
|
3186
3810
|
import app from "./app.mdx";
|
|
3187
3811
|
import { components } from "./components/mdx"
|
|
@@ -3197,12 +3821,12 @@ export default routerFactory((routes) => {
|
|
|
3197
3821
|
},
|
|
3198
3822
|
};
|
|
3199
3823
|
});
|
|
3200
|
-
`,
|
|
3824
|
+
`,zt=m((e,t)=>{let{createPath:n,createImportHelpers:r}=_(e),{renderToFile:i}=y({helpers:{...r({origin:`lib`}),...S(),serializeParams(e){return JSON.stringify(e.params)}}}),{renderToFile:a}=y({helpers:r({origin:`src`})}),o=e=>!e?.trim().length,s=c(t?.templates,It),u=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await a(n.pages(r.file),r.name===`index`?Lt:s(r.name,r),{route:r,title:r.name.replace(/\{([^}]+)\}/g,`$1`),message:ft()},{overwrite:o}):t===`pageLayout`&&await a(n.pages(r.file),Ft,{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(b)}]}return[]}).sort(b);for(let[e,a]of[[`client.ts`,ht],[`server.ts`,gt]])await i(n.libEntry(e),a,{pageRoutes:r,layouts:t});for(let[e,t]of[[`params.ts`,Ct],[`router.ts`,wt],[`ssg:routes.ts`,Et]])await i(n.lib(e),t,{pageRoutes:r})};return{config({command:n}){return{oxc:{jsx:{importSource:`preact`}},plugins:pt(e,n,t)}},async start(){for(let[e,t]of[[`env.d.ts`,_t],[`app.ts`,mt],[`mdx.ts`,vt],[`use.ts`,Dt],[`ssg.ts`,Tt],[`pageSamples/styles.module.css`,xt],[`pageSamples/welcome.tsx`,St],[`pageSamples/page.tsx`,bt],[`pageSamples/404.tsx`,yt]])await i(n.lib(e),t,{});for(let[e,t]of[[`pages/404.mdx`,Pt],[`components/Link.tsx`,kt],[`components/mdx.ts`,At],[`app.mdx`,Ot],[`router.ts`,Rt]])await a(n.src(e),t,{entryDir:l.entryDir},{overwrite:o});await a(n.src(`index.html`),Nt,{entryDir:l.entryDir},{overwrite:e=>!e?.trim().length||!e.replace(/<!--[\s\S]*?-->/g,``).trim().length});for(let[e,t]of[[`client.ts`,jt],[`server.ts`,Mt]])await a(n.entry(e),t,{},{overwrite:o})},async watch(e,t){(!t||t.kind===`create`)&&await u(e),await d(e)},async build(e){await u(e),await d(e)}}}),Bt=p({meta:{name:`MDX`,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:zt}),Vt={json:`application/json`,form:[`application/x-www-form-urlencoded`,`multipart/form-data`],raw:void 0},Ht=()=>{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,`_`)}${C(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),o=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]=Ut(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 o(e))for(let o of e.methods){let l={responses:c(e,o)},u=s(e,i);u&&(l.parameters=u);let d=n.find(e=>e.method===o&&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===o&&Object.keys(a).includes(e.target));f.length&&(l.requestBody={required:!0,content:f.reduce((t,n)=>{let{contentType:i=Vt[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][o.toLowerCase()]=l}return t};return{generateComponentId:n,generateComponentPath:r,generatePathVariations:o,generateOpenAPISchema:e=>{let t=new Map;for(let n of e)t.set(n.name,o(n));let{components:n,paths:r}=e.sort(b).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}}}},Ut=(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]},Wt=m((e,t)=>{let{outfile:n=``,...r}={...t},{createPath:i}=_(e),{generateOpenAPISchema:a}=Ht(),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 ee(i.src(n),l,{})};return{async watch(e){await o(e)},async build(e){await o(e)}}}),Gt=p({meta:{name:`OpenAPI`,resolveTypes:!0},factory:Wt}),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.101.4`,"@types/react":`^19.2.18`,"@types/react-dom":`^19.2.4`,"path-to-regexp":`^8.4.2`,react:`^19.2.8`,"react-dom":`^19.2.8`,"react-router":`^8.3.0`}},Kt=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(`❗${r([`red`,`bold`],`WARN`)}: React Router v7 only supports dot-suffix mixed segments (e.g. :param.html).`),console.warn(` ${r([`magenta`],e.orig)} in ${r([`blue`],t)} route won't match as expected.`),console.warn()),[e.parts.map(e=>e.type===`static`?e.value:n(e)).join(``)])).join(`/`)},qt=()=>{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=Kt(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},Jt=()=>{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)]},Yt=`import type { ReactNode } from "react";
|
|
3201
3825
|
|
|
3202
3826
|
export const AppProvider = ({ children }: { children: ReactNode }) => {
|
|
3203
3827
|
return children;
|
|
3204
3828
|
}
|
|
3205
|
-
`,
|
|
3829
|
+
`,Xt=`import { type QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3206
3830
|
import type { ReactNode } from "react";
|
|
3207
3831
|
|
|
3208
3832
|
import { getQueryClient } from "./query";
|
|
@@ -3221,7 +3845,7 @@ export const AppProvider = ({
|
|
|
3221
3845
|
</QueryClientProvider>
|
|
3222
3846
|
);
|
|
3223
3847
|
}
|
|
3224
|
-
`,
|
|
3848
|
+
`,Zt=`import { lazy, type JSX } from "react";
|
|
3225
3849
|
|
|
3226
3850
|
import {
|
|
3227
3851
|
createRoot,
|
|
@@ -3275,7 +3899,7 @@ export const mount = async (
|
|
|
3275
3899
|
}
|
|
3276
3900
|
|
|
3277
3901
|
export default clientRenderFactory();
|
|
3278
|
-
`,
|
|
3902
|
+
`,Qt=`{
|
|
3279
3903
|
{{#if name}}
|
|
3280
3904
|
id: "{{name}}",
|
|
3281
3905
|
{{/if}}
|
|
@@ -3293,7 +3917,7 @@ export default clientRenderFactory();
|
|
|
3293
3917
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
3294
3918
|
{{/if}}
|
|
3295
3919
|
}
|
|
3296
|
-
|
|
3920
|
+
`,$t=`import type { JSX } from "react";
|
|
3297
3921
|
|
|
3298
3922
|
import {
|
|
3299
3923
|
renderToString as renderToStringOrig,
|
|
@@ -3359,7 +3983,7 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
3359
3983
|
};
|
|
3360
3984
|
|
|
3361
3985
|
export default serverRenderFactory();
|
|
3362
|
-
`,
|
|
3986
|
+
`,en=`/* @jsxImportSource react */
|
|
3363
3987
|
|
|
3364
3988
|
import styles from "./styles.module.css";
|
|
3365
3989
|
|
|
@@ -3400,7 +4024,7 @@ export default function PageSample(props: {
|
|
|
3400
4024
|
</div>
|
|
3401
4025
|
);
|
|
3402
4026
|
}
|
|
3403
|
-
`,
|
|
4027
|
+
`,tn=`/* @jsxImportSource react */
|
|
3404
4028
|
|
|
3405
4029
|
import styles from "./styles.module.css";
|
|
3406
4030
|
|
|
@@ -3452,7 +4076,7 @@ export default function PageSample(props: {
|
|
|
3452
4076
|
</div>
|
|
3453
4077
|
);
|
|
3454
4078
|
}
|
|
3455
|
-
`,
|
|
4079
|
+
`,nn=`* {
|
|
3456
4080
|
margin: 0;
|
|
3457
4081
|
padding: 0;
|
|
3458
4082
|
box-sizing: border-box;
|
|
@@ -3587,7 +4211,7 @@ export default function PageSample(props: {
|
|
|
3587
4211
|
align-items: center;
|
|
3588
4212
|
gap: 0.25rem;
|
|
3589
4213
|
}
|
|
3590
|
-
`,
|
|
4214
|
+
`,rn=`/* @jsxImportSource react */
|
|
3591
4215
|
|
|
3592
4216
|
import styles from "./styles.module.css";
|
|
3593
4217
|
|
|
@@ -3653,7 +4277,7 @@ export default function WelcomePage() {
|
|
|
3653
4277
|
</div>
|
|
3654
4278
|
);
|
|
3655
4279
|
}
|
|
3656
|
-
`,
|
|
4280
|
+
`,an=`import { QueryClient, type QueryClientConfig } from "@tanstack/react-query";
|
|
3657
4281
|
|
|
3658
4282
|
let client: QueryClient | undefined;
|
|
3659
4283
|
|
|
@@ -3668,7 +4292,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
3668
4292
|
}
|
|
3669
4293
|
return client;
|
|
3670
4294
|
};
|
|
3671
|
-
`,
|
|
4295
|
+
`,on=`import { QueryClient, type QueryClientConfig } from "@tanstack/react-query";
|
|
3672
4296
|
|
|
3673
4297
|
import { store } from "{{ createImport 'lib' '@ssr/base' }}";
|
|
3674
4298
|
|
|
@@ -3691,40 +4315,27 @@ export const getQueryClient = (): QueryClient => {
|
|
|
3691
4315
|
}
|
|
3692
4316
|
return ctx.tsqClient as QueryClient;
|
|
3693
4317
|
};
|
|
3694
|
-
`,
|
|
4318
|
+
`,sn=`export type ComponentLoader = () => Promise<{
|
|
3695
4319
|
loader?: (arg: unknown) => Promise<unknown>;
|
|
3696
4320
|
}>;
|
|
3697
4321
|
|
|
3698
4322
|
export const loaderFactory = (opt?: { withPreload?: boolean }) => {
|
|
3699
4323
|
return (componentLoader: ComponentLoader) => {
|
|
3700
4324
|
const loader = async (arg: unknown) => {
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
: null;
|
|
3711
|
-
} catch (error) {
|
|
3712
|
-
// TODO: swallowing loader errors is wrong. Returning null hydrates the
|
|
3713
|
-
// route as "loaded, value null" - RR never sees the failure, so no
|
|
3714
|
-
// errorElement/ErrorBoundary fires on server or client, and this
|
|
3715
|
-
// console.error runs server-side during SSR so the client sees nothing.
|
|
3716
|
-
// Proper fix: let the error reach RR's \`context.errors\` (rethrow, or
|
|
3717
|
-
// return a Response/data() with an error status) so it serializes into
|
|
3718
|
-
// __staticRouterHydrationData.errors and renders the route boundary
|
|
3719
|
-
// isomorphically.
|
|
3720
|
-
console.error(error);
|
|
3721
|
-
return null;
|
|
3722
|
-
}
|
|
4325
|
+
const component = await componentLoader();
|
|
4326
|
+
// Return null, not undefined, when a route has no loader.
|
|
4327
|
+
// React Router keys hydration data by route id;
|
|
4328
|
+
// an undefined value is dropped by JSON.stringify during serialization,
|
|
4329
|
+
// leaving the id absent, which makes RR re-run the loader on the client and double-render.
|
|
4330
|
+
// null survives serialization, so the id stays present.
|
|
4331
|
+
return typeof component.loader === "function"
|
|
4332
|
+
? await component.loader(arg)
|
|
4333
|
+
: null;
|
|
3723
4334
|
};
|
|
3724
4335
|
return opt?.withPreload ? { loader } : {};
|
|
3725
4336
|
};
|
|
3726
4337
|
};
|
|
3727
|
-
`,
|
|
4338
|
+
`,cn=`import type { JSX, ComponentType } from "react";
|
|
3728
4339
|
|
|
3729
4340
|
import {
|
|
3730
4341
|
type RouteObject,
|
|
@@ -3782,7 +4393,7 @@ export const createRouters = (
|
|
|
3782
4393
|
}
|
|
3783
4394
|
|
|
3784
4395
|
export default createRouterFactory<RouteObject, Promise<JSX.Element>>();
|
|
3785
|
-
`,
|
|
4396
|
+
`,ln=`import { Outlet } from "react-router";
|
|
3786
4397
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
3787
4398
|
|
|
3788
4399
|
export default function App() {
|
|
@@ -3792,7 +4403,7 @@ export default function App() {
|
|
|
3792
4403
|
</AppProvider>
|
|
3793
4404
|
);
|
|
3794
4405
|
}
|
|
3795
|
-
`,
|
|
4406
|
+
`,un=`import {
|
|
3796
4407
|
type LinkProps as RouterLinkProps,
|
|
3797
4408
|
Link as RouterLink,
|
|
3798
4409
|
} from "react-router";
|
|
@@ -3820,7 +4431,7 @@ export default function Link(
|
|
|
3820
4431
|
</RouterLink>
|
|
3821
4432
|
);
|
|
3822
4433
|
}
|
|
3823
|
-
`,
|
|
4434
|
+
`,dn=`import renderFactory, {
|
|
3824
4435
|
createRoutes,
|
|
3825
4436
|
hydrate,
|
|
3826
4437
|
mount,
|
|
@@ -3847,7 +4458,7 @@ if (root) {
|
|
|
3847
4458
|
} else {
|
|
3848
4459
|
console.error("❌ Root element not found!");
|
|
3849
4460
|
}
|
|
3850
|
-
`,
|
|
4461
|
+
`,fn=`import renderFactory, {
|
|
3851
4462
|
createRoutes,
|
|
3852
4463
|
renderToStream,
|
|
3853
4464
|
renderToString,
|
|
@@ -3874,7 +4485,7 @@ export default renderFactory(() => {
|
|
|
3874
4485
|
},
|
|
3875
4486
|
};
|
|
3876
4487
|
});
|
|
3877
|
-
`,
|
|
4488
|
+
`,pn=`<!doctype html>
|
|
3878
4489
|
<html lang="en">
|
|
3879
4490
|
<head>
|
|
3880
4491
|
<meta charset="UTF-8" />
|
|
@@ -3886,17 +4497,17 @@ export default renderFactory(() => {
|
|
|
3886
4497
|
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
3887
4498
|
</body>
|
|
3888
4499
|
</html>
|
|
3889
|
-
`,
|
|
4500
|
+
`,mn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
3890
4501
|
|
|
3891
4502
|
export default function Page() {
|
|
3892
4503
|
return <PageSample />;
|
|
3893
4504
|
}
|
|
3894
|
-
`,
|
|
4505
|
+
`,hn=`import { Outlet } from "react-router";
|
|
3895
4506
|
|
|
3896
4507
|
export default function Layout() {
|
|
3897
4508
|
return <Outlet />;
|
|
3898
4509
|
}
|
|
3899
|
-
`,
|
|
4510
|
+
`,gn=`import PageSample from "{{ createImport 'lib' 'pageSamples/page.tsx' }}";
|
|
3900
4511
|
|
|
3901
4512
|
export default function Page() {
|
|
3902
4513
|
return PageSample({
|
|
@@ -3909,8 +4520,8 @@ export default function Page() {
|
|
|
3909
4520
|
},
|
|
3910
4521
|
});
|
|
3911
4522
|
}
|
|
3912
|
-
`,
|
|
3913
|
-
`,
|
|
4523
|
+
`,_n=`export { default } from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}";
|
|
4524
|
+
`,vn=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
3914
4525
|
|
|
3915
4526
|
import app from "./app";
|
|
3916
4527
|
|
|
@@ -3925,12 +4536,12 @@ export default routerFactory((routes) => {
|
|
|
3925
4536
|
},
|
|
3926
4537
|
};
|
|
3927
4538
|
});
|
|
3928
|
-
|
|
4539
|
+
`,yn=m((e,t)=>{let{createPath:n,createImportHelpers:r}=_(e),{renderToFile:i}=y({helpers:{...r({origin:`lib`}),...S()},partials:{routePartial:Qt}}),{renderToFile:a}=y({helpers:r({origin:`src`})}),o=qt(),s=e=>!e?.trim().length,u=c(t?.templates,gn),d=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await a(n.pages(r.file),r.name===`index`?_n:u(r.name,r),{route:r,message:Jt()},{overwrite:s}):t===`pageLayout`&&await a(n.pages(r.file),hn,{route:r},{overwrite:s})},f=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(b),r=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),a=o(g(r));for(let[e,t]of[[`client.ts`,Zt],[`server.ts`,$t]])await i(n.libEntry(e),t,{pageEntries:r,nestedRoutes:a});await i(n.lib(`router.tsx`),cn,{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`,``],[`react.ts`,sn],[`pageSamples/styles.module.css`,nn],[`pageSamples/welcome.tsx`,rn],[`pageSamples/page.tsx`,tn],[`pageSamples/404.tsx`,en],...t?.tanstack?.query?[[`app.tsx`,Xt],[`query.ts`,an]]:[[`app.tsx`,Yt],[`query.ts`,`/** tanstack query disabled */`]]])await i(n.lib(e),r,{});for(let[e,t]of[[`pages/404.tsx`,mn],[`components/Link.tsx`,un],[`app.tsx`,ln],[`router.ts`,vn]])await a(n.src(e),t,{entryDir:l.entryDir},{overwrite:s});await a(n.src(`index.html`),pn,{entryDir:l.entryDir},{overwrite:e=>!e?.trim().length||!e.replace(/<!--[\s\S]*?-->/g,``).trim().length});for(let[e,t]of[[`client.ts`,dn],[`server.ts`,fn]])await a(n.entry(e),t,{},{overwrite:s})},async watch(e,t){(!t||t.kind===`create`)&&await d(e),await f(e)},async build(e){await d(e),await f(e)},async ssrBuild(){await i(n.lib(`query.ts`),t?.tanstack?.query?on:`/** tanstack query disabled */`,{ssrBundle:!0})}}}),bn=p({meta:{name:`React`,jsx:`preserve`,jsxImportSource:`react`},dependencies(e){return{react:W.devDependencies.react,"react-router":W.devDependencies[`react-router`],"path-to-regexp":W.devDependencies[`path-to-regexp`],...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:yn}),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.101.4`,"path-to-regexp":`^8.4.2`,"solid-js":`^1.9.15`}},xn=()=>{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,n)=>e.kind===`param`&&(t(o[n+1])||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(`❗${r([`red`,`bold`],`WARN`)}: At the moment Solid Router does not support mixed path segments.`),console.warn(` ${r([`magenta`],e.orig)} segment in ${r([`blue`],t)} route won't match as expected.`),console.warn()),[e.parts.map(e=>e.type===`static`?e.value:n(e)).join(``)])).join(`/`)},Sn=()=>{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)]},Cn=`import type { ParentComponent } from "solid-js";
|
|
3929
4540
|
|
|
3930
4541
|
export const AppProvider: ParentComponent = (props) => {
|
|
3931
4542
|
return props.children;
|
|
3932
4543
|
};
|
|
3933
|
-
`,
|
|
4544
|
+
`,wn=`import type { ParentComponent } from "solid-js";
|
|
3934
4545
|
import { type QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
|
3935
4546
|
|
|
3936
4547
|
import { getQueryClient } from "./query";
|
|
@@ -3942,7 +4553,7 @@ export const AppProvider: ParentComponent<{ client?: QueryClient }> = (props) =>
|
|
|
3942
4553
|
</QueryClientProvider>
|
|
3943
4554
|
);
|
|
3944
4555
|
};
|
|
3945
|
-
`,
|
|
4556
|
+
`,Tn=`import { lazy, type JSX } from "solid-js";
|
|
3946
4557
|
import { hydrate as hydrateOrig, render } from "solid-js/web";
|
|
3947
4558
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
3948
4559
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
@@ -3987,7 +4598,7 @@ export const mount = async (
|
|
|
3987
4598
|
}
|
|
3988
4599
|
|
|
3989
4600
|
export default clientRenderFactory();
|
|
3990
|
-
`,
|
|
4601
|
+
`,En=`{
|
|
3991
4602
|
path: "{{path}}",
|
|
3992
4603
|
{{#if component}}
|
|
3993
4604
|
component: {{component}}_component,
|
|
@@ -3997,7 +4608,7 @@ export default clientRenderFactory();
|
|
|
3997
4608
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
3998
4609
|
{{/if}}
|
|
3999
4610
|
}
|
|
4000
|
-
`,
|
|
4611
|
+
`,Dn=`import type { JSX } from "solid-js";
|
|
4001
4612
|
|
|
4002
4613
|
import {
|
|
4003
4614
|
generateHydrationScript,
|
|
@@ -4065,7 +4676,12 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
4065
4676
|
> = async (resolver, { headerTags = [], ...options } = {}) => {
|
|
4066
4677
|
const stream = renderToStreamOrig(() => resolver().component, options);
|
|
4067
4678
|
const { readable, writable } = new TransformStream<string, string>();
|
|
4068
|
-
|
|
4679
|
+
/**
|
|
4680
|
+
* On failure, pipeTo aborts the writable, which errors the readable side
|
|
4681
|
+
* and surfaces the error to the stream consumer; catching here only keeps
|
|
4682
|
+
* the rejection from escaping as an unhandled one.
|
|
4683
|
+
* */
|
|
4684
|
+
stream.pipeTo(writable).catch(() => {});
|
|
4069
4685
|
return {
|
|
4070
4686
|
head: [...headerTags, generateHydrationScript()].join("\\n"),
|
|
4071
4687
|
html: readable,
|
|
@@ -4073,7 +4689,7 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
4073
4689
|
};
|
|
4074
4690
|
|
|
4075
4691
|
export default serverRenderFactory<true>();
|
|
4076
|
-
`,
|
|
4692
|
+
`,On=`/* @jsxImportSource solid-js */
|
|
4077
4693
|
|
|
4078
4694
|
import styles from "./styles.module.css";
|
|
4079
4695
|
|
|
@@ -4114,7 +4730,7 @@ export default function PageSample(props: {
|
|
|
4114
4730
|
</div>
|
|
4115
4731
|
);
|
|
4116
4732
|
}
|
|
4117
|
-
`,
|
|
4733
|
+
`,kn=`/* @jsxImportSource solid-js */
|
|
4118
4734
|
|
|
4119
4735
|
import styles from "./styles.module.css";
|
|
4120
4736
|
|
|
@@ -4166,7 +4782,7 @@ export default function PageSample(props: {
|
|
|
4166
4782
|
</div>
|
|
4167
4783
|
);
|
|
4168
4784
|
}
|
|
4169
|
-
`,
|
|
4785
|
+
`,An=`* {
|
|
4170
4786
|
margin: 0;
|
|
4171
4787
|
padding: 0;
|
|
4172
4788
|
box-sizing: border-box;
|
|
@@ -4301,7 +4917,7 @@ export default function PageSample(props: {
|
|
|
4301
4917
|
align-items: center;
|
|
4302
4918
|
gap: 0.25rem;
|
|
4303
4919
|
}
|
|
4304
|
-
`,
|
|
4920
|
+
`,jn=`/* @jsxImportSource solid-js */
|
|
4305
4921
|
|
|
4306
4922
|
import styles from "./styles.module.css";
|
|
4307
4923
|
|
|
@@ -4367,7 +4983,7 @@ export default function WelcomePage() {
|
|
|
4367
4983
|
</div>
|
|
4368
4984
|
);
|
|
4369
4985
|
}
|
|
4370
|
-
`,
|
|
4986
|
+
`,Mn=`import { QueryClient, type QueryClientConfig } from "@tanstack/solid-query";
|
|
4371
4987
|
|
|
4372
4988
|
let client: QueryClient | undefined;
|
|
4373
4989
|
|
|
@@ -4382,7 +4998,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
4382
4998
|
}
|
|
4383
4999
|
return client;
|
|
4384
5000
|
};
|
|
4385
|
-
`,
|
|
5001
|
+
`,Nn=`import { QueryClient, type QueryClientConfig } from "@tanstack/solid-query";
|
|
4386
5002
|
|
|
4387
5003
|
import { store } from "{{ createImport 'lib' '@ssr/base' }}";
|
|
4388
5004
|
|
|
@@ -4405,7 +5021,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
4405
5021
|
}
|
|
4406
5022
|
return ctx.tsqClient as QueryClient;
|
|
4407
5023
|
};
|
|
4408
|
-
`,
|
|
5024
|
+
`,Pn=`import type { JSX, ParentComponent } from "solid-js";
|
|
4409
5025
|
import { Router, type RouteDefinition } from "@solidjs/router";
|
|
4410
5026
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
4411
5027
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
@@ -4430,7 +5046,7 @@ export const createRouters = (
|
|
|
4430
5046
|
|
|
4431
5047
|
const serverRouter = (url: URL) => {
|
|
4432
5048
|
const component = (
|
|
4433
|
-
<Router root={app} base={base} url={url.pathname}>
|
|
5049
|
+
<Router root={app} base={base} url={url.pathname + url.search}>
|
|
4434
5050
|
{routes}
|
|
4435
5051
|
</Router>
|
|
4436
5052
|
);
|
|
@@ -4441,30 +5057,25 @@ export const createRouters = (
|
|
|
4441
5057
|
}
|
|
4442
5058
|
|
|
4443
5059
|
export default createRouterFactory<RouteDefinition, JSX.Element>();
|
|
4444
|
-
`,
|
|
5060
|
+
`,Fn=`export type ComponentLoader = () => Promise<{
|
|
4445
5061
|
preload?: () => Promise<unknown>;
|
|
4446
5062
|
}>;
|
|
4447
5063
|
|
|
4448
5064
|
export const loaderFactory = (opt?: { withPreload?: boolean }) => {
|
|
4449
5065
|
return (componentLoader: ComponentLoader) => {
|
|
4450
5066
|
const preload = async () => {
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
: undefined;
|
|
4456
|
-
} catch (error) {
|
|
4457
|
-
console.error(error);
|
|
4458
|
-
return;
|
|
4459
|
-
}
|
|
5067
|
+
const component = await componentLoader();
|
|
5068
|
+
return typeof component.preload === "function"
|
|
5069
|
+
? component.preload
|
|
5070
|
+
: undefined;
|
|
4460
5071
|
};
|
|
4461
5072
|
return opt?.withPreload ? { preload } : {};
|
|
4462
5073
|
};
|
|
4463
5074
|
};
|
|
4464
|
-
`,
|
|
5075
|
+
`,In=`export type MaybeWrapped<T> = import("solid-js/store").Store<T> | T;
|
|
4465
5076
|
|
|
4466
5077
|
export { unwrap } from "solid-js/store";
|
|
4467
|
-
`,
|
|
5078
|
+
`,Ln=`import type { ParentComponent } from "solid-js";
|
|
4468
5079
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
4469
5080
|
|
|
4470
5081
|
const App: ParentComponent = (props) => {
|
|
@@ -4472,7 +5083,7 @@ const App: ParentComponent = (props) => {
|
|
|
4472
5083
|
};
|
|
4473
5084
|
|
|
4474
5085
|
export default App;
|
|
4475
|
-
`,
|
|
5086
|
+
`,Rn=`import { A, type AnchorProps } from "@solidjs/router";
|
|
4476
5087
|
import { type JSXElement, splitProps } from "solid-js";
|
|
4477
5088
|
|
|
4478
5089
|
import { pageRouteMap, type LinkProps } from "{{ createImport 'libCore' }}";
|
|
@@ -4497,7 +5108,7 @@ export default function Link(
|
|
|
4497
5108
|
|
|
4498
5109
|
return <A {...{ ...restProps, href: href() }}>{knownProps.children}</A>;
|
|
4499
5110
|
}
|
|
4500
|
-
`,
|
|
5111
|
+
`,zn=`import renderFactory, {
|
|
4501
5112
|
createRoutes,
|
|
4502
5113
|
hydrate,
|
|
4503
5114
|
mount,
|
|
@@ -4524,7 +5135,7 @@ if (root) {
|
|
|
4524
5135
|
} else {
|
|
4525
5136
|
console.error("❌ Root element not found!");
|
|
4526
5137
|
}
|
|
4527
|
-
`,
|
|
5138
|
+
`,Bn=`import renderFactory, {
|
|
4528
5139
|
createRoutes,
|
|
4529
5140
|
renderToStream,
|
|
4530
5141
|
renderToString,
|
|
@@ -4551,7 +5162,7 @@ export default renderFactory(() => {
|
|
|
4551
5162
|
},
|
|
4552
5163
|
};
|
|
4553
5164
|
});
|
|
4554
|
-
`,
|
|
5165
|
+
`,Vn=`<!doctype html>
|
|
4555
5166
|
<html lang="en">
|
|
4556
5167
|
<head>
|
|
4557
5168
|
<meta charset="UTF-8" />
|
|
@@ -4563,19 +5174,19 @@ export default renderFactory(() => {
|
|
|
4563
5174
|
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
4564
5175
|
</body>
|
|
4565
5176
|
</html>
|
|
4566
|
-
`,
|
|
5177
|
+
`,Hn=`import PageSample from "{{ createImport 'lib' 'pageSamples/404.tsx' }}";
|
|
4567
5178
|
|
|
4568
5179
|
export default function Page() {
|
|
4569
5180
|
return <PageSample />;
|
|
4570
5181
|
}
|
|
4571
|
-
`,
|
|
5182
|
+
`,Un=`import type { ParentComponent } from "solid-js";
|
|
4572
5183
|
|
|
4573
5184
|
const Layout: ParentComponent = (props) => {
|
|
4574
5185
|
return props.children;
|
|
4575
5186
|
};
|
|
4576
5187
|
|
|
4577
5188
|
export default Layout;
|
|
4578
|
-
`,
|
|
5189
|
+
`,Wn=`import PageSample from "{{ createImport 'lib' 'pageSamples/page.tsx' }}";
|
|
4579
5190
|
|
|
4580
5191
|
export default function Page() {
|
|
4581
5192
|
return PageSample({
|
|
@@ -4588,8 +5199,8 @@ export default function Page() {
|
|
|
4588
5199
|
},
|
|
4589
5200
|
});
|
|
4590
5201
|
}
|
|
4591
|
-
`,
|
|
4592
|
-
`,
|
|
5202
|
+
`,Gn=`export { default } from "{{ createImport 'lib' 'pageSamples/welcome.tsx' }}";
|
|
5203
|
+
`,Kn=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
4593
5204
|
|
|
4594
5205
|
import app from "./app";
|
|
4595
5206
|
|
|
@@ -4604,16 +5215,19 @@ export default routerFactory((routes) => {
|
|
|
4604
5215
|
},
|
|
4605
5216
|
};
|
|
4606
5217
|
});
|
|
4607
|
-
`,
|
|
5218
|
+
`,qn=m((e,t)=>{let{generators:n=[]}=e.config,{createPath:r,createImportHelpers:i}=_(e),{renderToFile:a}=y({helpers:{...i({origin:`lib`}),...S()},partials:{routePartial:En}}),{renderToFile:o}=y({helpers:i({origin:`src`})}),s=xn(),u=e=>!e?.trim().length,d=c(t?.templates,Wn),f=async e=>{for(let{kind:t,entry:n}of e)t===`pageRoute`?await o(r.pages(n.file),n.name===`index`?Gn:d(n.name,n),{route:n,message:Sn()},{overwrite:u}):t===`pageLayout`&&await o(r.pages(n.file),Un,{route:n},{overwrite:u})},p=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(b),n=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),i=s(g(n));for(let[e,t]of[[`client.ts`,Tn],[`server.ts`,Dn]])await a(r.libEntry(e),t,{pageEntries:n,nestedRoutes:i});await a(r.lib(`router.tsx`),Pn,{entries:e,indexRoutes:t})};return{config({command:e}){let{templates:r,...i}={...t};return{oxc:{jsx:{importSource:`solid-js`}},plugins:e===`build`?[T({...i,...n.some(e=>e.meta.slot===`ssr`)?{ssr:!0,solid:{...i?.solid,hydratable:!0}}:{}})]:[T({...i,dev:!0,hot:!0})]}},async start(){for(let[e,n]of[[`env.d.ts`,``],[`solid.ts`,Fn],[`unwrap.ts`,In],[`pageSamples/styles.module.css`,An],[`pageSamples/welcome.tsx`,jn],[`pageSamples/page.tsx`,kn],[`pageSamples/404.tsx`,On],...t?.tanstack?.query?[[`app.tsx`,wn],[`query.ts`,Mn]]:[[`app.tsx`,Cn],[`query.ts`,`/** tanstack query disabled */`]]])await a(r.lib(e),n,{});for(let[e,t]of[[`pages/404.tsx`,Hn],[`components/Link.tsx`,Rn],[`app.tsx`,Ln],[`router.ts`,Kn]])await o(r.src(e),t,{entryDir:l.entryDir},{overwrite:u});await o(r.src(`index.html`),Vn,{entryDir:l.entryDir},{overwrite:e=>!e?.trim().length||!e.replace(/<!--[\s\S]*?-->/g,``).trim().length});for(let[e,t]of[[`client.ts`,zn],[`server.ts`,Bn]])await o(r.entry(e),t,{},{overwrite:u})},async watch(e,t){(!t||t.kind===`create`)&&await f(e),await p(e)},async build(e){await f(e),await p(e)},async ssrBuild(){await a(r.lib(`query.ts`),t?.tanstack?.query?Nn:`/** tanstack query disabled */`,{ssrBundle:!0})}}}),Jn=p({meta:{name:`SolidJS`,jsx:`preserve`,jsxImportSource:`solid-js`},dependencies(e){return{"solid-js":G.devDependencies[`solid-js`],"@solidjs/router":G.devDependencies[`@solidjs/router`],"path-to-regexp":G.devDependencies[`path-to-regexp`],...e?.tanstack?.query?{"@tanstack/solid-query":G.devDependencies[`@tanstack/solid-query`]}:{}}},factory:qn}),Yn=m(e=>{let{generators:i=[],refineTypeName:a,...o}={...e.config},{createPath:s}=_(e);return{async postBuild(){let a=s.distDir(`ssg`),c=n(a,`../ssr/server.js`);if(!await ce(c,le.F_OK).then(()=>!0,()=>!1)){console.error(),console.error(r(`red`,`❗Please enable ssrGenerator in ${e.name}/kosmo.config.ts`)),console.error(` SSG generator can not run without SSR server`),console.error();return}let l=te(`${e.name}: SSG`);l.append(`preparing...`);let{createDisposableServer:u}=await import(c);await E(n(a,`../client/assets`),t(a,`assets`),{recursive:!0}),l.append(`bundling routes...`),await w(h(o,...i.map(({factory:t})=>t(e).config?.({kind:`client`,command:`build`})),{root:s.lib(),appType:`custom`,plugins:[x.tsconfigPaths(e),x.nodePrefix()],resolve:{conditions:[`node`]},build:{ssr:s.lib(`ssg.ts`),target:`esnext`,sourcemap:!1,emptyOutDir:!0,rolldownOptions:{output:{dir:a,entryFileNames:`routes.js`,format:`esm`}}}}));try{let e=await import(t(a,`routes.js`)).then(e=>e.default);u(async n=>{for(let[r,i]of e.entries()){l.append(`[ ${r+1} of ${e.length} ] ${i}`);let o=await Xn(n,i);o!==void 0&&(await ue(t(a,i),{recursive:!0}),await de(t(a,t(i,`index.html`)),o,`utf8`))}l.succeed(`done ✨`)})}finally{await D(`${a}/routes.js`)}}}}),Xn=async(e,t)=>{try{let n=`http://localhost:${e}${t}`;return await(await fetch(n)).text()}catch(e){console.error(r(`red`,`✗ SSG: Failed generating ${t} route: ${e.message}`));return}},Zn=p({meta:{name:`SSG`,slot:`ssg`},factory:Yn}),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`,"light-my-request":`^6.6.0`,tinyglobby:`^0.2.17`}},Qn=`{{#if apiGenerator}}
|
|
4608
5219
|
export { default as apiApp } from "{{ createImport 'api' 'app' }}";
|
|
4609
5220
|
{{else}}
|
|
4610
5221
|
export const apiApp = undefined;
|
|
4611
5222
|
{{/if}}
|
|
4612
|
-
|
|
5223
|
+
`,$n=`import { AsyncLocalStorage } from "node:async_hooks";
|
|
5224
|
+
|
|
5225
|
+
import type { FetchApp, NodeApp } from "@kosmojs/core";
|
|
4613
5226
|
|
|
4614
5227
|
export type RequestContext = {
|
|
4615
5228
|
headers?: HeadersInit;
|
|
4616
5229
|
tsqClient?: unknown;
|
|
5230
|
+
error?: unknown;
|
|
4617
5231
|
};
|
|
4618
5232
|
|
|
4619
5233
|
export const redirectCodes = [
|
|
@@ -4646,7 +5260,11 @@ export const maxRedirects = 5;
|
|
|
4646
5260
|
* Server-only module - never reaches browser bundles.
|
|
4647
5261
|
* */
|
|
4648
5262
|
export const store = new AsyncLocalStorage<RequestContext>();
|
|
4649
|
-
|
|
5263
|
+
|
|
5264
|
+
export const isFetchApp = (app: FetchApp | NodeApp): app is FetchApp => {
|
|
5265
|
+
return typeof (app as FetchApp).fetch === "function";
|
|
5266
|
+
};
|
|
5267
|
+
`,er=`import { type RequestContext, store } from "./base";
|
|
4650
5268
|
|
|
4651
5269
|
import { renderWrapper } from "{{ createImport 'libEntry' 'server' }}";
|
|
4652
5270
|
|
|
@@ -4664,12 +5282,20 @@ export const withSsrContext = <T>(
|
|
|
4664
5282
|
): T => {
|
|
4665
5283
|
return store.run(context, () => renderWrapper(context, render));
|
|
4666
5284
|
};
|
|
4667
|
-
`,In=`import { inject } from "light-my-request";
|
|
4668
5285
|
|
|
4669
|
-
|
|
5286
|
+
export const errorProvider = () => {
|
|
5287
|
+
return store.getStore()?.error;
|
|
5288
|
+
};
|
|
5289
|
+
`,tr=`import type { FetchApp, NodeApp } from "@kosmojs/core";
|
|
4670
5290
|
import type { Transport } from "@kosmojs/core/fetch";
|
|
4671
5291
|
|
|
4672
|
-
import {
|
|
5292
|
+
import {
|
|
5293
|
+
isFetchApp,
|
|
5294
|
+
maxRedirects,
|
|
5295
|
+
redirectCodes,
|
|
5296
|
+
ssrOrigin,
|
|
5297
|
+
store,
|
|
5298
|
+
} from "./base";
|
|
4673
5299
|
|
|
4674
5300
|
import { apiApp } from "{{ createImport 'lib' '@ssr/api' }}";
|
|
4675
5301
|
|
|
@@ -4680,16 +5306,63 @@ const headersProvider = (): HeadersInit | undefined => {
|
|
|
4680
5306
|
return store.getStore()?.headers;
|
|
4681
5307
|
};
|
|
4682
5308
|
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
* directly into the given app - no sockets, no interception.
|
|
4686
|
-
* Redirects are followed in-process, including the 303 and 301/302 method rewrite to GET.
|
|
4687
|
-
* */
|
|
4688
|
-
const createTransport = (app: FetchApp | NodeApp): Transport => {
|
|
4689
|
-
const dispatch = isFetchApp(app) //
|
|
5309
|
+
const createDispatch = (app: FetchApp | NodeApp) => {
|
|
5310
|
+
return isFetchApp(app)
|
|
4690
5311
|
? app.fetch
|
|
4691
|
-
:
|
|
5312
|
+
: async (request: Request): Promise<Response> => {
|
|
5313
|
+
const { inject } = await import("light-my-request");
|
|
5314
|
+
|
|
5315
|
+
/**
|
|
5316
|
+
* Node dispatch: serializes the web Request into light-my-request's
|
|
5317
|
+
* injection format and lifts the injected response back into a web Response.
|
|
5318
|
+
* */
|
|
5319
|
+
const url = new URL(request.url);
|
|
5320
|
+
|
|
5321
|
+
const payload = ["GET", "HEAD"].includes(request.method)
|
|
5322
|
+
? undefined
|
|
5323
|
+
: Buffer.from(await request.arrayBuffer());
|
|
5324
|
+
|
|
5325
|
+
const result = await inject(app.callback() as never, {
|
|
5326
|
+
method: request.method as never,
|
|
5327
|
+
url: url.pathname + url.search,
|
|
5328
|
+
headers: Object.fromEntries(request.headers),
|
|
5329
|
+
...(payload?.length ? { payload } : {}),
|
|
5330
|
+
});
|
|
5331
|
+
|
|
5332
|
+
const headers = new Headers();
|
|
5333
|
+
|
|
5334
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
5335
|
+
for (const entry of Array.isArray(value) ? value : [value]) {
|
|
5336
|
+
if (entry !== undefined) {
|
|
5337
|
+
headers.append(key, String(entry));
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
}
|
|
5341
|
+
|
|
5342
|
+
/**
|
|
5343
|
+
* 204/304 responses must not carry a body per the Response
|
|
5344
|
+
* constructor contract.
|
|
5345
|
+
* */
|
|
5346
|
+
const body = [204, 304].includes(result.statusCode)
|
|
5347
|
+
? null
|
|
5348
|
+
: new Uint8Array(result.rawPayload);
|
|
5349
|
+
|
|
5350
|
+
return new Response(body, {
|
|
5351
|
+
status: result.statusCode,
|
|
5352
|
+
statusText: result.statusMessage,
|
|
5353
|
+
headers,
|
|
5354
|
+
});
|
|
5355
|
+
};
|
|
5356
|
+
};
|
|
5357
|
+
|
|
5358
|
+
const createTransport = (app: FetchApp | NodeApp): Transport => {
|
|
5359
|
+
const dispatch = createDispatch(app);
|
|
4692
5360
|
|
|
5361
|
+
/**
|
|
5362
|
+
* Build a fetch-compatible transport that dispatches requests
|
|
5363
|
+
* directly into the given app - no sockets, no interception.
|
|
5364
|
+
* Redirects are followed in-process, including the 303 and 301/302 method rewrite to GET.
|
|
5365
|
+
* */
|
|
4693
5366
|
return async (input, init) => {
|
|
4694
5367
|
/**
|
|
4695
5368
|
* Request-scoped headers act as defaults: anything set explicitly
|
|
@@ -4757,58 +5430,71 @@ const createTransport = (app: FetchApp | NodeApp): Transport => {
|
|
|
4757
5430
|
};
|
|
4758
5431
|
};
|
|
4759
5432
|
|
|
4760
|
-
|
|
4761
|
-
* Node dispatch: serializes the web Request into light-my-request's
|
|
4762
|
-
* injection format and lifts the injected response back into a web Response.
|
|
4763
|
-
* */
|
|
4764
|
-
const createNodeDispatch = (app: NodeApp) => {
|
|
4765
|
-
return async (request: Request): Promise<Response> => {
|
|
4766
|
-
const url = new URL(request.url);
|
|
4767
|
-
|
|
4768
|
-
const payload = ["GET", "HEAD"].includes(request.method)
|
|
4769
|
-
? undefined
|
|
4770
|
-
: Buffer.from(await request.arrayBuffer());
|
|
4771
|
-
|
|
4772
|
-
const result = await inject(app.callback() as never, {
|
|
4773
|
-
method: request.method as never,
|
|
4774
|
-
url: url.pathname + url.search,
|
|
4775
|
-
headers: Object.fromEntries(request.headers),
|
|
4776
|
-
...(payload?.length ? { payload } : {}),
|
|
4777
|
-
});
|
|
4778
|
-
|
|
4779
|
-
const headers = new Headers();
|
|
5433
|
+
const ssrTransport = apiApp ? createTransport(apiApp as never) : undefined;
|
|
4780
5434
|
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
5435
|
+
export const transport = ssrTransport
|
|
5436
|
+
? async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
5437
|
+
try {
|
|
5438
|
+
const response = await ssrTransport(input, init);
|
|
5439
|
+
if (response?.ok) {
|
|
5440
|
+
return response;
|
|
5441
|
+
}
|
|
5442
|
+
// the rethrow here needed cause ssrTransport does not throw on non-2xx responses
|
|
5443
|
+
throw new SSRFetchError([
|
|
5444
|
+
input,
|
|
5445
|
+
response,
|
|
5446
|
+
typeof response?.text === "function"
|
|
5447
|
+
? await response.text()
|
|
5448
|
+
: response?.statusText,
|
|
5449
|
+
]);
|
|
5450
|
+
} catch (error) {
|
|
5451
|
+
/**
|
|
5452
|
+
* Capture the fetch error at the transport level and stash it on the request store.
|
|
5453
|
+
* Some frameworks - Solid notably - swallow a rejecting loader and still emit a partial render tree.
|
|
5454
|
+
* Storing the error here keeps it observable regardless of how the framework handles the loader rejection.
|
|
5455
|
+
* */
|
|
5456
|
+
const storage = store.getStore();
|
|
5457
|
+
if (storage) {
|
|
5458
|
+
storage.error = error;
|
|
4785
5459
|
}
|
|
5460
|
+
throw error;
|
|
4786
5461
|
}
|
|
4787
5462
|
}
|
|
5463
|
+
: undefined; // let fetch clients pick the transport
|
|
5464
|
+
|
|
5465
|
+
class SSRFetchError extends Error {
|
|
5466
|
+
constructor([input, response, message]: [
|
|
5467
|
+
input: RequestInfo | URL,
|
|
5468
|
+
response: Response,
|
|
5469
|
+
message: string | undefined,
|
|
5470
|
+
]) {
|
|
5471
|
+
const pathname = pathnameOf(input);
|
|
5472
|
+
const status = response.status ?? "unknown";
|
|
5473
|
+
super(\`\${pathname}: \${status} [ \${message} ]\`.trim());
|
|
5474
|
+
this.name = "SSRFetchError";
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
4788
5477
|
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
};
|
|
5478
|
+
const pathnameOf = (input: RequestInfo | URL): string => {
|
|
5479
|
+
try {
|
|
5480
|
+
if (typeof input === "string") {
|
|
5481
|
+
return new URL(input, "http://x").pathname;
|
|
5482
|
+
}
|
|
5483
|
+
if (input instanceof URL) {
|
|
5484
|
+
return input.pathname;
|
|
5485
|
+
}
|
|
5486
|
+
if (input instanceof Request) {
|
|
5487
|
+
return new URL(input.url).pathname;
|
|
5488
|
+
}
|
|
5489
|
+
} catch {}
|
|
5490
|
+
return String(input);
|
|
4803
5491
|
};
|
|
4804
|
-
|
|
4805
|
-
export const transport = apiApp ? createTransport(apiApp) : globalThis.fetch;
|
|
4806
|
-
`,Ln=`export const routeMap = [
|
|
5492
|
+
`,nr=`export const routeMap = [
|
|
4807
5493
|
{{#each pageRoutes}}
|
|
4808
5494
|
{ pathPattern: "{{honoPattern}}", renderMode: "{{renderMode}}" },
|
|
4809
5495
|
{{/each}}
|
|
4810
5496
|
];
|
|
4811
|
-
`,
|
|
5497
|
+
`,rr=`import { access, chmod, constants, readFile, unlink } from "node:fs/promises";
|
|
4812
5498
|
import {
|
|
4813
5499
|
createServer,
|
|
4814
5500
|
type IncomingMessage,
|
|
@@ -4824,14 +5510,9 @@ import { HTTPException } from "hono/http-exception";
|
|
|
4824
5510
|
import { stream } from "hono/streaming";
|
|
4825
5511
|
import { glob } from "tinyglobby";
|
|
4826
5512
|
|
|
4827
|
-
import {
|
|
4828
|
-
type FetchApp,
|
|
4829
|
-
isFetchApp,
|
|
4830
|
-
type NodeApp,
|
|
4831
|
-
type SSRSetup,
|
|
4832
|
-
} from "@kosmojs/core";
|
|
5513
|
+
import type { FetchApp, NodeApp, SSRSetup } from "@kosmojs/core";
|
|
4833
5514
|
|
|
4834
|
-
import { redirectCodes, ssrOrigin } from "./@ssr/base";
|
|
5515
|
+
import { isFetchApp, redirectCodes, ssrOrigin } from "./@ssr/base";
|
|
4835
5516
|
|
|
4836
5517
|
import { routeMap } from "{{ createImport 'lib' '@ssr/routes' }}";
|
|
4837
5518
|
import { apiBase, base } from "{{ createImport 'libCore' }}";
|
|
@@ -4853,12 +5534,14 @@ export const createApp = async () => {
|
|
|
4853
5534
|
const {
|
|
4854
5535
|
ssrApp,
|
|
4855
5536
|
withSsrContext,
|
|
5537
|
+
errorProvider,
|
|
4856
5538
|
}: {
|
|
4857
5539
|
ssrApp: SSRSetup;
|
|
4858
5540
|
withSsrContext: <T>(
|
|
4859
5541
|
context: { headers?: Record<string, string>; url?: string },
|
|
4860
5542
|
render: () => T,
|
|
4861
5543
|
) => Promise<T>;
|
|
5544
|
+
errorProvider: () => Error | undefined;
|
|
4862
5545
|
} = await import(\`\${ROOT}/app.js\`);
|
|
4863
5546
|
|
|
4864
5547
|
// Read the client index.html that includes <!--app-head--> and <!--app-html-->
|
|
@@ -4918,13 +5601,47 @@ export const createApp = async () => {
|
|
|
4918
5601
|
};
|
|
4919
5602
|
|
|
4920
5603
|
const renderPage = async (url: URL, ctx: Context) => {
|
|
4921
|
-
const {
|
|
5604
|
+
const {
|
|
5605
|
+
head = "",
|
|
5606
|
+
html,
|
|
5607
|
+
error,
|
|
5608
|
+
} = await withSsrContext(
|
|
4922
5609
|
{
|
|
4923
5610
|
headers: Object.fromEntries(ctx.req.raw.headers),
|
|
4924
5611
|
url: ctx.req.url,
|
|
4925
5612
|
},
|
|
4926
|
-
() =>
|
|
5613
|
+
async () => {
|
|
5614
|
+
try {
|
|
5615
|
+
/**
|
|
5616
|
+
* Catch a hard SSR render failure and fall back to CSR,
|
|
5617
|
+
* where the client's error boundaries can surface a meaningful error.
|
|
5618
|
+
* Server-side error boundaries behave inconsistently across frameworks,
|
|
5619
|
+
* can not rely on them here.
|
|
5620
|
+
* Instead, the SSR output are discarded and the client shell served verbatim,
|
|
5621
|
+
* letting the client re-render and handle the error uniformly.
|
|
5622
|
+
* This is the render-level counterpart to the transport-level catch in loaders.
|
|
5623
|
+
* */
|
|
5624
|
+
const { head = "", html } = await renderToString(url, ssrOptions());
|
|
5625
|
+
return { head, html, error: errorProvider() };
|
|
5626
|
+
} catch (error) {
|
|
5627
|
+
return { error };
|
|
5628
|
+
}
|
|
5629
|
+
},
|
|
4927
5630
|
);
|
|
5631
|
+
|
|
5632
|
+
if (error) {
|
|
5633
|
+
console.error("WARN: SSR failed, fallback to CSR");
|
|
5634
|
+
console.error(error);
|
|
5635
|
+
console.error();
|
|
5636
|
+
return [
|
|
5637
|
+
htmlStart.replace(
|
|
5638
|
+
"<!--app-head-->",
|
|
5639
|
+
\`<script>console.error("WARN: SSR failed, fallback to CSR")<\/script>\`,
|
|
5640
|
+
),
|
|
5641
|
+
htmlEnd,
|
|
5642
|
+
].join("");
|
|
5643
|
+
}
|
|
5644
|
+
|
|
4928
5645
|
return [
|
|
4929
5646
|
htmlStart.replace("<!--app-head-->", head),
|
|
4930
5647
|
html ?? "",
|
|
@@ -5193,14 +5910,14 @@ if (isMain) {
|
|
|
5193
5910
|
process.exit(1);
|
|
5194
5911
|
}
|
|
5195
5912
|
}
|
|
5196
|
-
`,
|
|
5913
|
+
`,ir=`string`,ar=m((e,r)=>{let{generators:i=[],refineTypeName:a,...o}=e.config,{createPath:c,createImportHelpers:l}=_(e),{renderToFile:u}=y({helpers:{...l({origin:`lib`})}});return{async build(e){let t=r?.renderMode?typeof r.renderMode==`string`?()=>r.renderMode:s(r?.renderMode,`string`):()=>ir,n={renderMode:JSON.stringify(r?.renderMode||null),pageRoutes:e.flatMap(e=>e.kind===`pageRoute`?[{...e.entry,renderMode:t(e.entry.name)}]:[]).sort(b),apiGenerator:i.some(e=>e.meta.slot===`backend`)};for(let[e,t]of[[`ssr.ts`,rr],[`@ssr/api.ts`,Qn],[`@ssr/__kosmo_ssr_bundle.ts`,er],[`@ssr/base.ts`,$n],[`@ssr/fetch.ts`,tr],[`@ssr/routes.ts`,nr]])await u(c.lib(e),t,n)},async postBuild(){let r=c.distDir(`ssr`),a=[x.tsconfigPaths(e),x.nodePrefix()];for(let t of i)await t.factory(e).ssrBuild?.();await w(h(o,...i.map(({factory:t})=>t(e).config?.({kind:`client`,command:`build`})),{root:c.src(),plugins:a,define:{KOSMO_PRODUCTION_BUILD:`true`},build:{ssr:c.lib(`@ssr/__kosmo_ssr_bundle`),ssrEmitAssets:!0,sourcemap:!0,emptyOutDir:!0,minify:!1,rolldownOptions:{output:{dir:r,entryFileNames:`app.js`,format:`esm`}}}})),await w({root:c.lib(),configFile:!1,appType:`custom`,plugins:a,resolve:{conditions:[`node`]},build:{ssr:c.lib(`ssr.ts`),target:`esnext`,sourcemap:!0,emptyOutDir:!0,rolldownOptions:{output:{dir:t(r,`server`),entryFileNames:`server.js`,format:`esm`}}}}),await E(n(r,`../client`),r,{recursive:!0});for(let e of[`server.js`,`server.js.map`])await E(`${r}/server/${e}`,`${r}/${e}`);await D(`${r}/server`,{recursive:!0,force:!0})}}}),or=p({meta:{name:`SSR`,slot:`ssr`},dependencies:{tinyglobby:q.devDependencies.tinyglobby,hono:q.devDependencies.hono,"@hono/node-server":q.devDependencies[`@hono/node-server`],"light-my-request":q.devDependencies[`light-my-request`]},factory:ar}),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.38`,"path-to-regexp":`^8.4.2`,svelte:`^5.56.10`}},sr=()=>{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">
|
|
5197
5914
|
import type { Snippet } from "svelte";
|
|
5198
5915
|
|
|
5199
5916
|
let { children }: { children: Snippet } = $props();
|
|
5200
5917
|
<\/script>
|
|
5201
5918
|
|
|
5202
5919
|
{@render children()}
|
|
5203
|
-
`,
|
|
5920
|
+
`,cr=`<script lang="ts">
|
|
5204
5921
|
import { type QueryClient, QueryClientProvider } from "@tanstack/svelte-query";
|
|
5205
5922
|
import type { Snippet } from "svelte";
|
|
5206
5923
|
|
|
@@ -5217,7 +5934,7 @@ if (isMain) {
|
|
|
5217
5934
|
<QueryClientProvider client={queryClient}>
|
|
5218
5935
|
{@render children()}
|
|
5219
5936
|
</QueryClientProvider>
|
|
5220
|
-
`,
|
|
5937
|
+
`,lr=`import { hydrate as hydrateOrig, mount as mountOrig } from "svelte";
|
|
5221
5938
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
5222
5939
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
5223
5940
|
|
|
@@ -5258,7 +5975,7 @@ export const mount = async (
|
|
|
5258
5975
|
}
|
|
5259
5976
|
|
|
5260
5977
|
export default clientRenderFactory();
|
|
5261
|
-
`,
|
|
5978
|
+
`,ur=`import { render as renderOrig } from "svelte/server";
|
|
5262
5979
|
|
|
5263
5980
|
import type {
|
|
5264
5981
|
RenderToStringWrapper,
|
|
@@ -5324,12 +6041,12 @@ export const renderToString: RenderToStringWrapper<
|
|
|
5324
6041
|
// svelte/server exposes only render() - no web-stream renderer -
|
|
5325
6042
|
// so this folder is string-only SSR.
|
|
5326
6043
|
export default serverRenderFactory<false>();
|
|
5327
|
-
`,
|
|
6044
|
+
`,dr=`declare module "*.svelte" {
|
|
5328
6045
|
import type { Component } from "svelte";
|
|
5329
6046
|
const component: Component;
|
|
5330
6047
|
export default component;
|
|
5331
6048
|
}
|
|
5332
|
-
`,
|
|
6049
|
+
`,fr=`<script lang="ts">
|
|
5333
6050
|
/**
|
|
5334
6051
|
* Folds [app, ...layouts] around the page component.
|
|
5335
6052
|
*
|
|
@@ -5363,7 +6080,7 @@ export default serverRenderFactory<false>();
|
|
|
5363
6080
|
{/snippet}
|
|
5364
6081
|
|
|
5365
6082
|
{@render layer(0)}
|
|
5366
|
-
`,
|
|
6083
|
+
`,pr=`<script lang="ts">
|
|
5367
6084
|
import styles from "./styles.module.css";
|
|
5368
6085
|
|
|
5369
6086
|
let { headline }: { headline?: string } = $props();
|
|
@@ -5395,7 +6112,7 @@ export default serverRenderFactory<false>();
|
|
|
5395
6112
|
</div>
|
|
5396
6113
|
</div>
|
|
5397
6114
|
</div>
|
|
5398
|
-
`,
|
|
6115
|
+
`,mr=`<script lang="ts">
|
|
5399
6116
|
import styles from "./styles.module.css";
|
|
5400
6117
|
|
|
5401
6118
|
let {
|
|
@@ -5440,7 +6157,7 @@ export default serverRenderFactory<false>();
|
|
|
5440
6157
|
</div>
|
|
5441
6158
|
</div>
|
|
5442
6159
|
</div>
|
|
5443
|
-
`,
|
|
6160
|
+
`,hr=`* {
|
|
5444
6161
|
margin: 0;
|
|
5445
6162
|
padding: 0;
|
|
5446
6163
|
box-sizing: border-box;
|
|
@@ -5575,7 +6292,7 @@ export default serverRenderFactory<false>();
|
|
|
5575
6292
|
align-items: center;
|
|
5576
6293
|
gap: 0.25rem;
|
|
5577
6294
|
}
|
|
5578
|
-
`,
|
|
6295
|
+
`,gr=`<script lang="ts">
|
|
5579
6296
|
import styles from "./styles.module.css";
|
|
5580
6297
|
<\/script>
|
|
5581
6298
|
|
|
@@ -5633,7 +6350,7 @@ export default serverRenderFactory<false>();
|
|
|
5633
6350
|
</div>
|
|
5634
6351
|
</div>
|
|
5635
6352
|
</div>
|
|
5636
|
-
`,
|
|
6353
|
+
`,_r=`export type ParamsMap = {
|
|
5637
6354
|
{{#each pageRoutes}}"{{name}}": {{serializeParamsLiteral .}};
|
|
5638
6355
|
{{/each}}
|
|
5639
6356
|
};
|
|
@@ -5642,7 +6359,7 @@ export const paramNames = {
|
|
|
5642
6359
|
{{#each pageRoutes}}"{{name}}": [ {{#each params.schema}}"{{name}}", {{/each}}],
|
|
5643
6360
|
{{/each}}
|
|
5644
6361
|
} as const;
|
|
5645
|
-
|
|
6362
|
+
`,vr=`import { QueryClient, type QueryClientConfig } from "@tanstack/svelte-query";
|
|
5646
6363
|
|
|
5647
6364
|
let client: QueryClient | undefined;
|
|
5648
6365
|
|
|
@@ -5657,7 +6374,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
5657
6374
|
}
|
|
5658
6375
|
return client;
|
|
5659
6376
|
};
|
|
5660
|
-
`,
|
|
6377
|
+
`,yr=`import { QueryClient, type QueryClientConfig } from "@tanstack/svelte-query";
|
|
5661
6378
|
|
|
5662
6379
|
import { store } from "{{ createImport 'lib' '@ssr/base' }}";
|
|
5663
6380
|
|
|
@@ -5680,7 +6397,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
5680
6397
|
}
|
|
5681
6398
|
return ctx.tsqClient as QueryClient;
|
|
5682
6399
|
};
|
|
5683
|
-
`,
|
|
6400
|
+
`,br=`import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
5684
6401
|
import { createRouterFactory } from "@kosmojs/core/generators";
|
|
5685
6402
|
|
|
5686
6403
|
import Layouts from "./Layouts.svelte";
|
|
@@ -5720,9 +6437,11 @@ export default createRouterFactory<
|
|
|
5720
6437
|
Promise<RouteComponent>,
|
|
5721
6438
|
{ server: { route: Route } }
|
|
5722
6439
|
>();
|
|
5723
|
-
`,
|
|
6440
|
+
`,xr=`import { match, pathToRegexp } from "path-to-regexp";
|
|
5724
6441
|
import { type Component, createContext } from "svelte";
|
|
5725
6442
|
|
|
6443
|
+
import { parseSearchParams } from "@kosmojs/core";
|
|
6444
|
+
|
|
5726
6445
|
import { paramNames } from "{{ createImport 'lib' 'params' }}";
|
|
5727
6446
|
import { base } from "{{ createImport 'libCore' }}";
|
|
5728
6447
|
|
|
@@ -5746,7 +6465,7 @@ export type RawRoute = {
|
|
|
5746
6465
|
};
|
|
5747
6466
|
|
|
5748
6467
|
type Loader = (
|
|
5749
|
-
route: Pick<Route, "name" | "params" | "paramsEntries">,
|
|
6468
|
+
route: Pick<Route, "name" | "params" | "paramsEntries" | "searchParams">,
|
|
5750
6469
|
) => Promise<unknown> | undefined;
|
|
5751
6470
|
|
|
5752
6471
|
/**
|
|
@@ -5788,6 +6507,7 @@ export type Route = {
|
|
|
5788
6507
|
name: string;
|
|
5789
6508
|
params: Record<string, string | Array<string>>;
|
|
5790
6509
|
paramsEntries: [keys: Array<string>, values: Array<unknown>];
|
|
6510
|
+
searchParams: Record<string, unknown>;
|
|
5791
6511
|
loaderData: Record<string, unknown>;
|
|
5792
6512
|
};
|
|
5793
6513
|
|
|
@@ -5827,6 +6547,7 @@ export const createRouter = (
|
|
|
5827
6547
|
|
|
5828
6548
|
return {
|
|
5829
6549
|
async resolve(url: URL = new URL(window.location.href)) {
|
|
6550
|
+
const searchParams = parseSearchParams(url);
|
|
5830
6551
|
const urlSegments = url.pathname.split("/").filter(Boolean).length;
|
|
5831
6552
|
|
|
5832
6553
|
// 1: use lightweight \`RegExp.test()\` on linear scan - no capture allocation
|
|
@@ -5869,7 +6590,7 @@ export const createRouter = (
|
|
|
5869
6590
|
|
|
5870
6591
|
loaderData[name] = await runLoader(name, () => {
|
|
5871
6592
|
return routeModule.loader
|
|
5872
|
-
? routeModule.loader({ name, params, paramsEntries })
|
|
6593
|
+
? routeModule.loader({ name, params, paramsEntries, searchParams })
|
|
5873
6594
|
: undefined; // should not survive serialization if no loader defined
|
|
5874
6595
|
});
|
|
5875
6596
|
|
|
@@ -5881,7 +6602,12 @@ export const createRouter = (
|
|
|
5881
6602
|
const key = \`\${layoutName}/layout\`;
|
|
5882
6603
|
loaderData[key] = await runLoader(key, () => {
|
|
5883
6604
|
return layoutModule.loader
|
|
5884
|
-
? layoutModule.loader({
|
|
6605
|
+
? layoutModule.loader({
|
|
6606
|
+
name: layoutName,
|
|
6607
|
+
params,
|
|
6608
|
+
paramsEntries,
|
|
6609
|
+
searchParams,
|
|
6610
|
+
})
|
|
5885
6611
|
: undefined; // should not survive serialization if no loader defined
|
|
5886
6612
|
});
|
|
5887
6613
|
}
|
|
@@ -5890,6 +6616,7 @@ export const createRouter = (
|
|
|
5890
6616
|
name,
|
|
5891
6617
|
params,
|
|
5892
6618
|
paramsEntries,
|
|
6619
|
+
searchParams,
|
|
5893
6620
|
loaderData,
|
|
5894
6621
|
};
|
|
5895
6622
|
|
|
@@ -5931,7 +6658,7 @@ export const createRoute = (
|
|
|
5931
6658
|
layouts,
|
|
5932
6659
|
};
|
|
5933
6660
|
};
|
|
5934
|
-
`,
|
|
6661
|
+
`,Sr=`import { getRouteContext } from "./svelte";
|
|
5935
6662
|
|
|
5936
6663
|
import type { ParamsMap, paramNames } from "{{ createImport 'lib' 'params' }}";
|
|
5937
6664
|
|
|
@@ -5957,6 +6684,10 @@ export function useParamsEntries<T extends keyof ParamsMap>(): [
|
|
|
5957
6684
|
return useRoute().paramsEntries as never;
|
|
5958
6685
|
}
|
|
5959
6686
|
|
|
6687
|
+
export function useSearchParams() {
|
|
6688
|
+
return useRoute().searchParams;
|
|
6689
|
+
}
|
|
6690
|
+
|
|
5960
6691
|
/**
|
|
5961
6692
|
* Reads loader data for the current page or one of its layouts.
|
|
5962
6693
|
* Without a key, returns the page's own data.
|
|
@@ -5967,7 +6698,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
5967
6698
|
const route = useRoute();
|
|
5968
6699
|
return route.loaderData?.[key || route.name] as T;
|
|
5969
6700
|
};
|
|
5970
|
-
`,
|
|
6701
|
+
`,Cr=`<script lang="ts">
|
|
5971
6702
|
import { AppProvider } from "{{ createImport 'lib' 'app' }}";
|
|
5972
6703
|
import type { Snippet } from "svelte";
|
|
5973
6704
|
|
|
@@ -5977,7 +6708,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
5977
6708
|
<AppProvider>
|
|
5978
6709
|
{@render children()}
|
|
5979
6710
|
</AppProvider>
|
|
5980
|
-
`,
|
|
6711
|
+
`,wr=`<script lang="ts">
|
|
5981
6712
|
import type { Snippet } from "svelte";
|
|
5982
6713
|
import type { HTMLAnchorAttributes } from "svelte/elements";
|
|
5983
6714
|
|
|
@@ -6001,7 +6732,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
6001
6732
|
<\/script>
|
|
6002
6733
|
|
|
6003
6734
|
<a {href} {...rest}>{@render children?.()}</a>
|
|
6004
|
-
`,
|
|
6735
|
+
`,Tr=`import renderFactory, {
|
|
6005
6736
|
createRoutes,
|
|
6006
6737
|
hydrate,
|
|
6007
6738
|
mount,
|
|
@@ -6028,7 +6759,7 @@ if (root) {
|
|
|
6028
6759
|
} else {
|
|
6029
6760
|
console.error("❌ Root element not found!");
|
|
6030
6761
|
}
|
|
6031
|
-
`,
|
|
6762
|
+
`,Er=`import renderFactory, {
|
|
6032
6763
|
createRoutes,
|
|
6033
6764
|
renderToString,
|
|
6034
6765
|
// no renderToStream on Svelte folders
|
|
@@ -6049,7 +6780,7 @@ export default renderFactory(() => {
|
|
|
6049
6780
|
},
|
|
6050
6781
|
};
|
|
6051
6782
|
});
|
|
6052
|
-
`,
|
|
6783
|
+
`,Dr=`<!doctype html>
|
|
6053
6784
|
<html lang="en">
|
|
6054
6785
|
<head>
|
|
6055
6786
|
<meta charset="UTF-8" />
|
|
@@ -6061,19 +6792,19 @@ export default renderFactory(() => {
|
|
|
6061
6792
|
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
6062
6793
|
</body>
|
|
6063
6794
|
</html>
|
|
6064
|
-
`,
|
|
6795
|
+
`,Or=`<script lang="ts">
|
|
6065
6796
|
import PageSample from "{{ createImport 'lib' 'pageSamples/404.svelte' }}";
|
|
6066
6797
|
<\/script>
|
|
6067
6798
|
|
|
6068
6799
|
<PageSample />
|
|
6069
|
-
`,
|
|
6800
|
+
`,kr=`<script lang="ts">
|
|
6070
6801
|
import type { Snippet } from "svelte";
|
|
6071
6802
|
|
|
6072
6803
|
let { children }: { children: Snippet } = $props();
|
|
6073
6804
|
<\/script>
|
|
6074
6805
|
|
|
6075
6806
|
{@render children()}
|
|
6076
|
-
`,
|
|
6807
|
+
`,Ar=`<script lang="ts">
|
|
6077
6808
|
import PageSample from "{{ createImport 'lib' 'pageSamples/page.svelte' }}";
|
|
6078
6809
|
|
|
6079
6810
|
const pathMap = {
|
|
@@ -6092,7 +6823,7 @@ export default renderFactory(() => {
|
|
|
6092
6823
|
routeName={"{{route.name}}"}
|
|
6093
6824
|
{pathMap}
|
|
6094
6825
|
/>
|
|
6095
|
-
`,
|
|
6826
|
+
`,jr=`<script lang="ts">
|
|
6096
6827
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.svelte' }}";
|
|
6097
6828
|
<\/script>
|
|
6098
6829
|
|
|
@@ -6105,7 +6836,7 @@ export default renderFactory(() => {
|
|
|
6105
6836
|
</svelte:head>
|
|
6106
6837
|
|
|
6107
6838
|
<WelcomePage />
|
|
6108
|
-
`,
|
|
6839
|
+
`,Mr=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
6109
6840
|
|
|
6110
6841
|
import app from "./app.svelte";
|
|
6111
6842
|
|
|
@@ -6120,7 +6851,7 @@ export default routerFactory((routes) => {
|
|
|
6120
6851
|
},
|
|
6121
6852
|
};
|
|
6122
6853
|
});
|
|
6123
|
-
`,
|
|
6854
|
+
`,Nr=m((e,t)=>{let{createPath:n,createImportHelpers:r}=_(e),{renderToFile:i}=y({helpers:{...r({origin:`lib`}),...S()}}),{renderToFile:a}=y({helpers:r({origin:`src`})}),o=e=>!e?.trim().length,s=c(t?.templates,Ar),u=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await a(n.pages(r.file),r.name===`index`?jr:s(r.name,r),{route:r,title:r.name.replace(/\{([^}]+)\}/g,`$1`),message:sr()},{overwrite:o}):t===`pageLayout`&&await a(n.pages(r.file),kr,{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(b)}]}return[]}).sort(b);for(let[e,a]of[[`client.ts`,lr],[`server.ts`,ur]])await i(n.libEntry(e),a,{pageRoutes:r,layouts:t});for(let[e,t]of[[`params.ts`,_r],[`router.ts`,br]])await i(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`,dr],[`svelte.ts`,xr],[`Layouts.svelte`,fr],[`use.ts`,Sr],[`pageSamples/styles.module.css`,hr],[`pageSamples/welcome.svelte`,gr],[`pageSamples/page.svelte`,mr],[`pageSamples/404.svelte`,pr],...t?.tanstack?.query?[[`app/app.svelte`,Y],[`app/app-tsq.svelte`,cr],[`app/index.ts`,`export { default as AppProvider } from "./app-tsq.svelte";`],[`query.ts`,vr]]:[[`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 i(n.lib(e),r,{});for(let[e,t]of[[`pages/404.svelte`,Or],[`components/Link.svelte`,wr],[`app.svelte`,Cr],[`router.ts`,Mr]])await a(n.src(e),t,{entryDir:l.entryDir},{overwrite:o});await a(n.src(`index.html`),Dr,{entryDir:l.entryDir},{overwrite:e=>!e?.trim().length||!e.replace(/<!--[\s\S]*?-->/g,``).trim().length});for(let[e,t]of[[`client.ts`,Tr],[`server.ts`,Er]])await a(n.entry(e),t,{},{overwrite:o})},async watch(e,t){(!t||t.kind===`create`)&&await u(e),await d(e)},async build(e){await u(e),await d(e)},async ssrBuild(){await i(n.lib(`query.ts`),t?.tanstack?.query?yr:`/** tanstack query disabled */`,{ssrBundle:!0})}}}),Pr=p({meta:{name:`Svelte`},dependencies(e){return{svelte:J.devDependencies.svelte,"path-to-regexp":J.devDependencies[`path-to-regexp`],...e?.tanstack?.query?{"@tanstack/svelte-query":J.devDependencies[`@tanstack/svelte-query`]}:{}}},factory:Nr}),Fr={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.16`}},Ir=`import Type from "typebox";
|
|
6124
6855
|
|
|
6125
6856
|
/**
|
|
6126
6857
|
* Custom types for JavaScript constructs that have no JSON Schema
|
|
@@ -6192,7 +6923,7 @@ export default {
|
|
|
6192
6923
|
Buffer: TBuffer(),
|
|
6193
6924
|
ArrayBuffer: TArrayBuffer(),
|
|
6194
6925
|
};
|
|
6195
|
-
`,
|
|
6926
|
+
`,Lr=`import type { TValidationError } from "typebox/error";
|
|
6196
6927
|
|
|
6197
6928
|
import type { ValidationErrorEntry } from "@kosmojs/core";
|
|
6198
6929
|
|
|
@@ -7376,7 +8107,7 @@ const format = (fmt: string, ...args: unknown[]): string => {
|
|
|
7376
8107
|
|
|
7377
8108
|
return str;
|
|
7378
8109
|
};
|
|
7379
|
-
`,
|
|
8110
|
+
`,Rr=`import Type from "typebox";
|
|
7380
8111
|
import { Compile } from "typebox/compile";
|
|
7381
8112
|
import Value from "typebox/value";
|
|
7382
8113
|
|
|
@@ -7428,14 +8159,14 @@ export const validationSchemaFactory = (
|
|
|
7428
8159
|
},
|
|
7429
8160
|
};
|
|
7430
8161
|
};
|
|
7431
|
-
`,
|
|
8162
|
+
`,zr=`import { Settings } from "typebox/system";
|
|
7432
8163
|
|
|
7433
8164
|
Settings.Set({{settings}});
|
|
7434
8165
|
|
|
7435
8166
|
export { default as customTypes } from "{{customTypesImport}}";
|
|
7436
8167
|
|
|
7437
8168
|
export const validationMessages = {{validationMessages}};
|
|
7438
|
-
`,
|
|
8169
|
+
`,Br=`import type { ValidationSchemas } from "@kosmojs/core";
|
|
7439
8170
|
|
|
7440
8171
|
import { validationSchemaFactory } from "{{ createImport 'lib' '@typebox' }}";
|
|
7441
8172
|
|
|
@@ -7496,14 +8227,14 @@ export const validationSchemas: ValidationSchemas = {
|
|
|
7496
8227
|
{{/each}}
|
|
7497
8228
|
},
|
|
7498
8229
|
};
|
|
7499
|
-
`,
|
|
8230
|
+
`,Vr={exactOptionalPropertyTypes:!0},Hr=m((e,t)=>{let{createPath:n,createImport:r,createImportHelpers:i}=_(e),{renderToFile:a}=y({helpers:{...i({origin:`lib`})}}),{validationMessages:s={},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(o).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)}}]:[]})})),s=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`),Br,{route:r,resolvedTypes:e,requestSchemas:i,responseSchemas:s})}};return{async start(){for(let[e,t]of[[`custom-types.ts`,Ir],[`error-handler.ts`,Lr],[`index.ts`,Rr],[`setup.ts`,zr]])await a(n.lib(`@typebox`,e),t,{validationMessages:JSON.stringify(s),customTypesImport:c,settings:JSON.stringify({...Vr,...l})})},async watch(e,t){await u(t?e.filter(({kind:e,entry:n})=>t.kind===`update`&&e===`apiRoute`&&n.fileFullpath===t.file):e)},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 Ur=p({meta:{name:`TypeBox`,resolveTypes:!0},dependencies:{typebox:Fr.devDependencies.typebox},factory:Hr}),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.101.4`,"path-to-regexp":`^8.4.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(`/`)},Wr=()=>{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},Gr=()=>{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)]},Kr=`import type { Plugin } from "vue";
|
|
7500
8231
|
|
|
7501
8232
|
export { default as AppProvider } from "./provider.vue";
|
|
7502
8233
|
|
|
7503
8234
|
export const appProvider: Plugin = {
|
|
7504
8235
|
install() {},
|
|
7505
8236
|
};
|
|
7506
|
-
`,
|
|
8237
|
+
`,qr=`import { VueQueryPlugin } from "@tanstack/vue-query";
|
|
7507
8238
|
import type { Plugin } from "vue";
|
|
7508
8239
|
|
|
7509
8240
|
import { getQueryClient } from "../query";
|
|
@@ -7515,10 +8246,10 @@ export const appProvider: Plugin = {
|
|
|
7515
8246
|
app.use(VueQueryPlugin, { queryClient: getQueryClient() });
|
|
7516
8247
|
},
|
|
7517
8248
|
};
|
|
7518
|
-
`,
|
|
8249
|
+
`,Jr=`<template>
|
|
7519
8250
|
<slot />
|
|
7520
8251
|
</template>
|
|
7521
|
-
`,
|
|
8252
|
+
`,Yr=`import type { App } from "vue";
|
|
7522
8253
|
import type { RouterFactoryReturn } from "@kosmojs/core";
|
|
7523
8254
|
import { clientRenderFactory } from "@kosmojs/core/generators";
|
|
7524
8255
|
|
|
@@ -7558,7 +8289,7 @@ export const mount = async (
|
|
|
7558
8289
|
}
|
|
7559
8290
|
|
|
7560
8291
|
export default clientRenderFactory();
|
|
7561
|
-
`,
|
|
8292
|
+
`,Xr=`{
|
|
7562
8293
|
path: "{{path}}",
|
|
7563
8294
|
{{#if name}}
|
|
7564
8295
|
name: "{{name}}",
|
|
@@ -7576,7 +8307,7 @@ export default clientRenderFactory();
|
|
|
7576
8307
|
children: [ {{#each children}}{{> routePartial}}, {{/each}}],
|
|
7577
8308
|
{{/if}}
|
|
7578
8309
|
}
|
|
7579
|
-
`,
|
|
8310
|
+
`,Zr=`import type { App } from "vue";
|
|
7580
8311
|
|
|
7581
8312
|
import {
|
|
7582
8313
|
renderToString as renderToStringOrig,
|
|
@@ -7652,31 +8383,27 @@ export const renderToStream: RenderToStreamWrapper<
|
|
|
7652
8383
|
Parameters<typeof renderToWebStream>[1]
|
|
7653
8384
|
> = async (resolver, { headerTags = [], context = {} } = {}) => {
|
|
7654
8385
|
const { component, loaderData } = await resolver();
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
controller.enqueue(new TextEncoder().encode(script));
|
|
7663
|
-
}
|
|
7664
|
-
},
|
|
7665
|
-
});
|
|
8386
|
+
|
|
8387
|
+
/**
|
|
8388
|
+
* loaderData is fully resolved before streaming starts, so the hydration
|
|
8389
|
+
* script ships in head - same as string mode. Head is written with the shell,
|
|
8390
|
+
* guaranteeing delivery even when the stream errors mid-body.
|
|
8391
|
+
* */
|
|
8392
|
+
const script = createHydrationScript(loaderData);
|
|
7666
8393
|
|
|
7667
8394
|
return {
|
|
7668
|
-
head: [...headerTags].join("\\n"),
|
|
7669
|
-
html:
|
|
8395
|
+
head: [...headerTags, ...script ? [script] : []].join("\\n"),
|
|
8396
|
+
html: renderToWebStream(component, context),
|
|
7670
8397
|
};
|
|
7671
8398
|
}
|
|
7672
8399
|
|
|
7673
8400
|
export default serverRenderFactory();
|
|
7674
|
-
|
|
8401
|
+
`,Qr=`declare module "*.vue" {
|
|
7675
8402
|
import type { DefineComponent } from "vue";
|
|
7676
8403
|
const component: DefineComponent<{}, {}, any>;
|
|
7677
8404
|
export default component;
|
|
7678
8405
|
}
|
|
7679
|
-
|
|
8406
|
+
`,$r=`<script setup lang="ts">
|
|
7680
8407
|
import styles from "./styles.module.css";
|
|
7681
8408
|
defineProps<{
|
|
7682
8409
|
headline?: string;
|
|
@@ -7715,7 +8442,7 @@ defineProps<{
|
|
|
7715
8442
|
</div>
|
|
7716
8443
|
</div>
|
|
7717
8444
|
</template>
|
|
7718
|
-
|
|
8445
|
+
`,$=`<script setup lang="ts">
|
|
7719
8446
|
import styles from "./styles.module.css";
|
|
7720
8447
|
defineProps<{
|
|
7721
8448
|
message: string;
|
|
@@ -7764,7 +8491,7 @@ defineProps<{
|
|
|
7764
8491
|
</div>
|
|
7765
8492
|
</div>
|
|
7766
8493
|
</template>
|
|
7767
|
-
`,
|
|
8494
|
+
`,ei=`* {
|
|
7768
8495
|
margin: 0;
|
|
7769
8496
|
padding: 0;
|
|
7770
8497
|
box-sizing: border-box;
|
|
@@ -7897,7 +8624,7 @@ defineProps<{
|
|
|
7897
8624
|
align-items: center;
|
|
7898
8625
|
gap: 0.25rem;
|
|
7899
8626
|
}
|
|
7900
|
-
`,
|
|
8627
|
+
`,ti=`<script setup lang="ts">
|
|
7901
8628
|
import styles from "./styles.module.css";
|
|
7902
8629
|
<\/script>
|
|
7903
8630
|
|
|
@@ -7961,7 +8688,7 @@ import styles from "./styles.module.css";
|
|
|
7961
8688
|
</div>
|
|
7962
8689
|
</div>
|
|
7963
8690
|
</template>
|
|
7964
|
-
`,
|
|
8691
|
+
`,ni=`import { QueryClient, type QueryClientConfig } from "@tanstack/vue-query";
|
|
7965
8692
|
|
|
7966
8693
|
let client: QueryClient | undefined;
|
|
7967
8694
|
|
|
@@ -7976,7 +8703,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
7976
8703
|
}
|
|
7977
8704
|
return client;
|
|
7978
8705
|
};
|
|
7979
|
-
`,
|
|
8706
|
+
`,ri=`import { QueryClient, type QueryClientConfig } from "@tanstack/vue-query";
|
|
7980
8707
|
|
|
7981
8708
|
import { store } from "{{ createImport 'lib' '@ssr/base' }}";
|
|
7982
8709
|
|
|
@@ -7999,7 +8726,7 @@ export const getQueryClient = (): QueryClient => {
|
|
|
7999
8726
|
}
|
|
8000
8727
|
return ctx.tsqClient as QueryClient;
|
|
8001
8728
|
};
|
|
8002
|
-
`,
|
|
8729
|
+
`,ii=`import {
|
|
8003
8730
|
type App,
|
|
8004
8731
|
type Component,
|
|
8005
8732
|
createApp,
|
|
@@ -8128,7 +8855,7 @@ export const createRouters = (
|
|
|
8128
8855
|
// would otherwise skip the guard
|
|
8129
8856
|
installLoaderGuard(router);
|
|
8130
8857
|
|
|
8131
|
-
await router.push(url.pathname.
|
|
8858
|
+
await router.push(url.pathname + url.search);
|
|
8132
8859
|
|
|
8133
8860
|
await router.isReady();
|
|
8134
8861
|
|
|
@@ -8150,14 +8877,14 @@ export default createRouterFactory<
|
|
|
8150
8877
|
Promise<App>,
|
|
8151
8878
|
{ server: { loaderData: Record<string, unknown> } }
|
|
8152
8879
|
>();
|
|
8153
|
-
`,
|
|
8880
|
+
`,ai=`import { type Ref, unref } from "vue";
|
|
8154
8881
|
|
|
8155
8882
|
export type MaybeWrapped<T> = Ref<T> | T;
|
|
8156
8883
|
|
|
8157
8884
|
export function unwrap<T>(value: MaybeWrapped<T>): T {
|
|
8158
8885
|
return unref(value);
|
|
8159
8886
|
}
|
|
8160
|
-
`,
|
|
8887
|
+
`,oi=`import { useRoute, useRouter } from "vue-router";
|
|
8161
8888
|
|
|
8162
8889
|
import type { RouterWithLoaderData } from "./router";
|
|
8163
8890
|
|
|
@@ -8172,7 +8899,7 @@ export const useLoaderData = <T>(key?: string): T | undefined => {
|
|
|
8172
8899
|
const route = useRoute();
|
|
8173
8900
|
return router.__loaderData?.[key || (route.name as string)] as T;
|
|
8174
8901
|
};
|
|
8175
|
-
`,
|
|
8902
|
+
`,si=`<script setup lang="ts">
|
|
8176
8903
|
import { AppProvider } from "_/app";
|
|
8177
8904
|
<\/script>
|
|
8178
8905
|
|
|
@@ -8181,7 +8908,7 @@ import { AppProvider } from "_/app";
|
|
|
8181
8908
|
<RouterView />
|
|
8182
8909
|
</AppProvider>
|
|
8183
8910
|
</template>
|
|
8184
|
-
`,
|
|
8911
|
+
`,ci=`<script setup lang="ts" generic="T extends LinkProps">
|
|
8185
8912
|
import { computed } from "vue";
|
|
8186
8913
|
import { RouterLink } from "vue-router";
|
|
8187
8914
|
|
|
@@ -8213,7 +8940,7 @@ const href = computed(() => {
|
|
|
8213
8940
|
<slot />
|
|
8214
8941
|
</RouterLink>
|
|
8215
8942
|
</template>
|
|
8216
|
-
`,
|
|
8943
|
+
`,li=`import renderFactory, {
|
|
8217
8944
|
createRoutes,
|
|
8218
8945
|
hydrate,
|
|
8219
8946
|
mount,
|
|
@@ -8240,7 +8967,7 @@ if (root) {
|
|
|
8240
8967
|
} else {
|
|
8241
8968
|
console.error("❌ Root element not found!");
|
|
8242
8969
|
}
|
|
8243
|
-
`,
|
|
8970
|
+
`,ui=`import renderFactory, {
|
|
8244
8971
|
createRoutes,
|
|
8245
8972
|
renderToStream,
|
|
8246
8973
|
renderToString,
|
|
@@ -8267,7 +8994,7 @@ export default renderFactory(() => {
|
|
|
8267
8994
|
},
|
|
8268
8995
|
};
|
|
8269
8996
|
});
|
|
8270
|
-
`,
|
|
8997
|
+
`,di=`<!doctype html>
|
|
8271
8998
|
<html lang="en">
|
|
8272
8999
|
<head>
|
|
8273
9000
|
<meta charset="UTF-8" />
|
|
@@ -8279,17 +9006,17 @@ export default renderFactory(() => {
|
|
|
8279
9006
|
<script type="module" src="/{{ entryDir }}/client.ts"><\/script>
|
|
8280
9007
|
</body>
|
|
8281
9008
|
</html>
|
|
8282
|
-
`,
|
|
9009
|
+
`,fi=`<script setup lang="ts">
|
|
8283
9010
|
import PageSample from "{{ createImport 'lib' 'pageSamples/404.vue' }}";
|
|
8284
9011
|
<\/script>
|
|
8285
9012
|
|
|
8286
9013
|
<template>
|
|
8287
9014
|
<PageSample />
|
|
8288
9015
|
</template>
|
|
8289
|
-
`,
|
|
9016
|
+
`,pi=`<template>
|
|
8290
9017
|
<router-view />
|
|
8291
9018
|
</template>
|
|
8292
|
-
`,
|
|
9019
|
+
`,mi=`<script setup lang="ts">
|
|
8293
9020
|
import PageSample from "{{ createImport 'lib' 'pageSamples/page.vue' }}";
|
|
8294
9021
|
<\/script>
|
|
8295
9022
|
|
|
@@ -8304,14 +9031,14 @@ import PageSample from "{{ createImport 'lib' 'pageSamples/page.vue' }}";
|
|
|
8304
9031
|
}"
|
|
8305
9032
|
/>
|
|
8306
9033
|
</template>
|
|
8307
|
-
`,
|
|
9034
|
+
`,hi=`<script setup lang="ts">
|
|
8308
9035
|
import WelcomePage from "{{ createImport 'lib' 'pageSamples/welcome.vue' }}";
|
|
8309
9036
|
<\/script>
|
|
8310
9037
|
|
|
8311
9038
|
<template>
|
|
8312
9039
|
<WelcomePage />
|
|
8313
9040
|
</template>
|
|
8314
|
-
`,
|
|
9041
|
+
`,gi=`import routerFactory, { createRouters } from "{{ createImport 'lib' 'router' }}";
|
|
8315
9042
|
import { appProvider } from "{{ createImport 'lib' 'app' }}";
|
|
8316
9043
|
|
|
8317
9044
|
import app from "./app.vue";
|
|
@@ -8330,5 +9057,5 @@ export default routerFactory((routes) => {
|
|
|
8330
9057
|
},
|
|
8331
9058
|
};
|
|
8332
9059
|
});
|
|
8333
|
-
`,
|
|
9060
|
+
`,_i=m((e,t)=>{let{createPath:n,createImportHelpers:r}=_(e),{renderToFile:i}=y({helpers:{...r({origin:`lib`}),...S()},partials:{routePartial:Xr}}),{renderToFile:a}=y({helpers:r({origin:`src`})}),o=Wr(),s=e=>!e?.trim().length,u=c(t?.templates,mi),d=async e=>{for(let{kind:t,entry:r}of e)t===`pageRoute`?await a(n.pages(r.file),r.name===`index`?hi:u(r.name,r),{route:r,message:Gr()},{overwrite:s}):t===`pageLayout`&&await a(n.pages(r.file),pi,{route:r},{overwrite:s})},f=async e=>{let t=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[]).sort(b),r=e.flatMap(({kind:e,entry:t})=>e===`pageRoute`||e===`pageLayout`?[t]:[]),a=o(g(r));for(let[e,t]of[[`client.ts`,Yr],[`server.ts`,Zr]])await i(n.libEntry(e),t,{pageEntries:r,nestedRoutes:a,lazyLoad:e===`client.ts`});await i(n.lib(`router.ts`),ii,{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`,Qr],[`unwrap.ts`,ai],[`use.ts`,oi],[`pageSamples/styles.module.css`,ei],[`pageSamples/welcome.vue`,ti],[`pageSamples/page.vue`,$],[`pageSamples/404.vue`,$r],[`app/provider.vue`,Jr],...t?.tanstack?.query?[[`app/index.ts`,qr],[`query.ts`,ni]]:[[`app/index.ts`,Kr],[`query.ts`,`/** tanstack query disabled */`]]])await i(n.lib(e),r,{});for(let[e,t]of[[`pages/404.vue`,fi],[`components/Link.vue`,ci],[`app.vue`,si],[`router.ts`,gi]])await a(n.src(e),t,{entryDir:l.entryDir},{overwrite:s});await a(n.src(`index.html`),di,{entryDir:l.entryDir},{overwrite:e=>!e?.trim().length||!e.replace(/<!--[\s\S]*?-->/g,``).trim().length});for(let[e,t]of[[`client.ts`,li],[`server.ts`,ui]])await a(n.entry(e),t,{},{overwrite:s})},async watch(e,t){(!t||t.kind===`create`)&&await d(e),await f(e)},async build(e){await d(e),await f(e)},async ssrBuild(){await i(n.lib(`query.ts`),t?.tanstack?.query?ri:`/** tanstack query disabled */`,{ssrBundle:!0})}}}),vi=p({meta:{name:`Vue`,jsxImportSource:`vue`},dependencies(e){return{vue:Z.devDependencies.vue,"vue-router":Z.devDependencies[`vue-router`],"path-to-regexp":Z.devDependencies[`path-to-regexp`],...e?.tanstack?.query?{"@tanstack/vue-query":Z.devDependencies[`@tanstack/vue-query`]}:{}}},factory:_i}),yi=e=>{let n=process.env.NODE_ENV||`development`,a=typeof e.base==`string`?e.base:e.base[n];if(!a?.trim())throw Error(r([`red`],`ERROR: Invalid Config - no base provided`));return{...e,base:t(`/`,a),apiBase:t(`/`,e.apiBase||i)}};export{e as coreGenerator,yi as defineConfig,P as fetchGenerator,De as h3Generator,Ke as honoGenerator,dt as koaGenerator,Bt as mdxGenerator,Gt as openapiGenerator,bn as reactGenerator,Jn as solidGenerator,Zn as ssgGenerator,or as ssrGenerator,Pr as svelteGenerator,Ur as typeboxGenerator,vi as vueGenerator};
|
|
8334
9061
|
//# sourceMappingURL=index.js.map
|