@kosmojs/dev 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/pkg/assets/{pkg-DU-tTiIp.js → pkg-Bkag325q.js} +99 -53
- package/pkg/assets/pkg-Bkag325q.js.map +1 -0
- package/pkg/chassis.js +1 -1
- package/pkg/chassis.js.map +1 -1
- package/pkg/index.js +303 -225
- package/pkg/index.js.map +1 -1
- package/pkg/assets/pkg-DU-tTiIp.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@kosmojs/dev",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.2",
|
|
5
5
|
"cacheVersion": "001",
|
|
6
6
|
"author": "Slee Woo",
|
|
7
7
|
"license": "MIT",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@kosmojs/core": "^0.4.2",
|
|
29
|
+
"@kosmojs/lib": "^0.4.2",
|
|
28
30
|
"@mdx-js/rollup": "^3.1.1",
|
|
29
31
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
30
32
|
"@vitejs/plugin-react": "^6.1.0",
|
|
@@ -32,23 +34,21 @@
|
|
|
32
34
|
"crc": "^4.3.2",
|
|
33
35
|
"path-to-regexp": "^8.4.2",
|
|
34
36
|
"semver": "^7.8.5",
|
|
35
|
-
"typebox": "^1.3.
|
|
37
|
+
"typebox": "^1.3.18",
|
|
36
38
|
"vite": "^8.2.2",
|
|
37
39
|
"vite-plugin-solid": "^2.11.14",
|
|
38
|
-
"yaml": "^2.9.0"
|
|
39
|
-
"@kosmojs/core": "^0.4.0",
|
|
40
|
-
"@kosmojs/lib": "^0.4.0"
|
|
40
|
+
"yaml": "^2.9.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@kosmojs/core-generator": "^0.3.0",
|
|
44
|
-
"@kosmojs/h3-generator": "^0.3.0",
|
|
45
44
|
"@kosmojs/fetch-generator": "^0.3.0",
|
|
45
|
+
"@kosmojs/h3-generator": "^0.3.0",
|
|
46
46
|
"@kosmojs/hono-generator": "^0.3.0",
|
|
47
47
|
"@kosmojs/koa-generator": "^0.3.0",
|
|
48
48
|
"@kosmojs/mdx-generator": "^0.3.0",
|
|
49
49
|
"@kosmojs/openapi-generator": "^0.3.0",
|
|
50
|
-
"@kosmojs/solid-generator": "^0.3.0",
|
|
51
50
|
"@kosmojs/react-generator": "^0.3.0",
|
|
51
|
+
"@kosmojs/solid-generator": "^0.3.0",
|
|
52
52
|
"@kosmojs/ssg-generator": "^0.3.0",
|
|
53
53
|
"@kosmojs/ssr-generator": "^0.3.0",
|
|
54
54
|
"@kosmojs/svelte-generator": "^0.3.0",
|
|
@@ -2,6 +2,7 @@ import{dirname as e,resolve as t}from"node:path";import{styleText as n}from"node
|
|
|
2
2
|
export const apiBase = "{{config.apiBase}}";
|
|
3
3
|
`,p=`export * from "./config";
|
|
4
4
|
export * from "./routes";
|
|
5
|
+
export * from "./types";
|
|
5
6
|
`,m=`{{> routeMapperPartial}}
|
|
6
7
|
|
|
7
8
|
import { base, apiBase } from "./config";
|
|
@@ -10,7 +11,7 @@ import { base, apiBase } from "./config";
|
|
|
10
11
|
export const apiRouteMap = {
|
|
11
12
|
{{#each apiRoutes}}
|
|
12
13
|
"{{name}}": apiRouteMapper<[{{serializeParamsTupleElements .}}]>(
|
|
13
|
-
apiBase,
|
|
14
|
+
join(base, apiBase),
|
|
14
15
|
{{serializeApiRoute .}},
|
|
15
16
|
),
|
|
16
17
|
{{/each}}
|
|
@@ -43,14 +44,32 @@ import {
|
|
|
43
44
|
type ValidationTarget,
|
|
44
45
|
} from "@kosmojs/core";
|
|
45
46
|
import type { HTTPMethod } from "@kosmojs/core/api";
|
|
46
|
-
import { createHost,
|
|
47
|
+
import { createHost, join } from "@kosmojs/core/fetch";
|
|
48
|
+
import type { RoutePathMethods } from "@kosmojs/core/generators";
|
|
47
49
|
|
|
48
50
|
export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
49
|
-
|
|
50
|
-
{
|
|
51
|
-
|
|
51
|
+
base: string,
|
|
52
|
+
{
|
|
53
|
+
name,
|
|
54
|
+
pathPattern,
|
|
55
|
+
params,
|
|
56
|
+
numericProperties,
|
|
57
|
+
booleanProperties,
|
|
58
|
+
}: ApiRouteSerialized,
|
|
59
|
+
): RoutePathMethods<ParamsT> & {
|
|
60
|
+
normalizeParams: (path: string) => Record<string, unknown>;
|
|
61
|
+
normalizeSearchParams: (
|
|
62
|
+
searchParams: Record<string, unknown>,
|
|
63
|
+
method: HTTPMethod,
|
|
64
|
+
) => Record<string, unknown>;
|
|
65
|
+
payloadResolver: <T>(
|
|
66
|
+
payload: Record<ValidationTarget, T> | undefined,
|
|
67
|
+
target: ValidationTarget,
|
|
68
|
+
method: HTTPMethod,
|
|
69
|
+
) => T | Record<string, unknown> | undefined;
|
|
70
|
+
} => {
|
|
52
71
|
const toPath = compile(pathPattern);
|
|
53
|
-
const pathMatcher = match(join(
|
|
72
|
+
const pathMatcher = match(join(base, pathPattern));
|
|
54
73
|
|
|
55
74
|
const maybeNumber = (val: unknown) => {
|
|
56
75
|
if (val === undefined || val === null) {
|
|
@@ -60,6 +79,12 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
60
79
|
return Number.isFinite(n) ? n : val;
|
|
61
80
|
};
|
|
62
81
|
|
|
82
|
+
const maybeBoolean = (val: unknown) => {
|
|
83
|
+
return [true, false, "true", "false"].includes(val as never) //
|
|
84
|
+
? JSON.parse(val as never)
|
|
85
|
+
: val;
|
|
86
|
+
};
|
|
87
|
+
|
|
63
88
|
const resolveParam = (path: string, param: (typeof params)[number]) => {
|
|
64
89
|
try {
|
|
65
90
|
const match = pathMatcher(path);
|
|
@@ -74,14 +99,21 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
74
99
|
method: HTTPMethod,
|
|
75
100
|
) => {
|
|
76
101
|
return Object.fromEntries(
|
|
77
|
-
Object.entries(searchParams).map(([k, v]) =>
|
|
78
|
-
k
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? v.map((e) => maybeNumber(e))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
Object.entries(searchParams).map(([k, v]) => {
|
|
103
|
+
if (numericProperties.query?.[method]?.includes(k)) {
|
|
104
|
+
return [
|
|
105
|
+
k,
|
|
106
|
+
Array.isArray(v) ? v.map((e) => maybeNumber(e)) : maybeNumber(v),
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
if (booleanProperties.query?.[method]?.includes(k)) {
|
|
110
|
+
return [
|
|
111
|
+
k,
|
|
112
|
+
Array.isArray(v) ? v.map((e) => maybeBoolean(e)) : maybeBoolean(v),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
return [k, v];
|
|
116
|
+
}),
|
|
85
117
|
);
|
|
86
118
|
};
|
|
87
119
|
|
|
@@ -101,7 +133,10 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
101
133
|
}, {});
|
|
102
134
|
};
|
|
103
135
|
|
|
104
|
-
const paramsMapper
|
|
136
|
+
const paramsMapper: RoutePathMethods<ParamsT>["paramsMapper"] = (
|
|
137
|
+
input,
|
|
138
|
+
opt,
|
|
139
|
+
) => {
|
|
105
140
|
return params.reduce<Record<string, unknown>>((map, name, i) => {
|
|
106
141
|
const coerceNumbers = opt?.coerceNumbers
|
|
107
142
|
? numericProperties.params.includes(name)
|
|
@@ -117,7 +152,7 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
117
152
|
}, {});
|
|
118
153
|
};
|
|
119
154
|
|
|
120
|
-
const parametrize = (params
|
|
155
|
+
const parametrize: RoutePathMethods<ParamsT>["parametrize"] = (params) => {
|
|
121
156
|
try {
|
|
122
157
|
return toPath(paramsMapper(params) as never);
|
|
123
158
|
} catch (error) {
|
|
@@ -126,21 +161,27 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
126
161
|
}
|
|
127
162
|
};
|
|
128
163
|
|
|
129
|
-
const
|
|
130
|
-
const path = join(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
const path: RoutePathMethods<ParamsT>["path"] = (params, query, opt) => {
|
|
165
|
+
const path = join(
|
|
166
|
+
opt?.prefix === false
|
|
167
|
+
? "/"
|
|
168
|
+
: typeof opt?.prefix === "string"
|
|
169
|
+
? opt.prefix
|
|
170
|
+
: base,
|
|
171
|
+
parametrize(params),
|
|
172
|
+
);
|
|
173
|
+
return query //
|
|
174
|
+
? [path, stringifySearchParams(query)].join("?")
|
|
175
|
+
: path;
|
|
136
176
|
};
|
|
137
177
|
|
|
138
|
-
const href = (
|
|
139
|
-
host
|
|
140
|
-
params
|
|
141
|
-
query
|
|
178
|
+
const href: RoutePathMethods<ParamsT>["href"] = (
|
|
179
|
+
host,
|
|
180
|
+
params,
|
|
181
|
+
query,
|
|
182
|
+
opt,
|
|
142
183
|
) => {
|
|
143
|
-
return createHost(host) + path(params, query);
|
|
184
|
+
return createHost(host) + path(params, query, opt);
|
|
144
185
|
};
|
|
145
186
|
|
|
146
187
|
const payloadResolver = <T>(
|
|
@@ -184,22 +225,21 @@ export const apiRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
184
225
|
return {
|
|
185
226
|
normalizeParams,
|
|
186
227
|
normalizeSearchParams,
|
|
228
|
+
payloadResolver,
|
|
187
229
|
paramsMapper,
|
|
188
230
|
parametrize,
|
|
189
|
-
base,
|
|
190
231
|
path,
|
|
191
232
|
href,
|
|
192
|
-
payloadResolver,
|
|
193
233
|
};
|
|
194
234
|
};
|
|
195
235
|
|
|
196
236
|
export const pageRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
197
|
-
|
|
237
|
+
base: string,
|
|
198
238
|
route: PageRouteSerialized,
|
|
199
|
-
) => {
|
|
239
|
+
): RoutePathMethods<ParamsT> => {
|
|
200
240
|
const toPath = compile(route.pathPattern);
|
|
201
241
|
|
|
202
|
-
const paramsMapper = (params
|
|
242
|
+
const paramsMapper: RoutePathMethods<ParamsT>["paramsMapper"] = (params) => {
|
|
203
243
|
return route.params.reduce<Record<string, unknown>>((map, name, i) => {
|
|
204
244
|
if (Array.isArray(params[i])) {
|
|
205
245
|
map[name] = params[i].map(String);
|
|
@@ -210,7 +250,7 @@ export const pageRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
210
250
|
}, {});
|
|
211
251
|
};
|
|
212
252
|
|
|
213
|
-
const parametrize = (params
|
|
253
|
+
const parametrize: RoutePathMethods<ParamsT>["parametrize"] = (params) => {
|
|
214
254
|
try {
|
|
215
255
|
return toPath(paramsMapper(params) as never);
|
|
216
256
|
} catch (error) {
|
|
@@ -219,32 +259,38 @@ export const pageRouteMapper = <ParamsT extends readonly unknown[]>(
|
|
|
219
259
|
}
|
|
220
260
|
};
|
|
221
261
|
|
|
222
|
-
const
|
|
223
|
-
const path = join(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
262
|
+
const path: RoutePathMethods<ParamsT>["path"] = (params, query, opt) => {
|
|
263
|
+
const path = join(
|
|
264
|
+
opt?.prefix === false
|
|
265
|
+
? "/"
|
|
266
|
+
: typeof opt?.prefix === "string"
|
|
267
|
+
? opt.prefix
|
|
268
|
+
: base,
|
|
269
|
+
parametrize(params),
|
|
270
|
+
);
|
|
271
|
+
return query //
|
|
272
|
+
? [path, stringifySearchParams(query)].join("?")
|
|
273
|
+
: path;
|
|
229
274
|
};
|
|
230
275
|
|
|
231
|
-
const href = (
|
|
232
|
-
host
|
|
233
|
-
params
|
|
234
|
-
query
|
|
276
|
+
const href: RoutePathMethods<ParamsT>["href"] = (
|
|
277
|
+
host,
|
|
278
|
+
params,
|
|
279
|
+
query,
|
|
280
|
+
opt,
|
|
235
281
|
) => {
|
|
236
|
-
return createHost(host) + path(params, query);
|
|
282
|
+
return createHost(host) + path(params, query, opt);
|
|
237
283
|
};
|
|
238
284
|
|
|
239
285
|
return {
|
|
240
286
|
paramsMapper,
|
|
241
287
|
parametrize,
|
|
242
|
-
base,
|
|
243
288
|
path,
|
|
244
289
|
href,
|
|
245
290
|
};
|
|
246
291
|
};
|
|
247
|
-
`,g=`
|
|
292
|
+
`,g=`export type Override<A, B> = Omit<A, keyof B> & B;
|
|
293
|
+
`,_=`declare const KOSMO_PRODUCTION_BUILD: boolean;
|
|
248
294
|
|
|
249
295
|
/**
|
|
250
296
|
* Enhances base TypeScript types with JSON Schema validation constraints.
|
|
@@ -442,7 +488,7 @@ interface TStringOptions {
|
|
|
442
488
|
*/
|
|
443
489
|
pattern?: string | RegExp;
|
|
444
490
|
}
|
|
445
|
-
`,
|
|
491
|
+
`,v=`# Ignore all files
|
|
446
492
|
*
|
|
447
493
|
|
|
448
494
|
# But don't ignore directories (so Git can traverse them)
|
|
@@ -451,15 +497,15 @@ interface TStringOptions {
|
|
|
451
497
|
# And don't ignore these files at any depth
|
|
452
498
|
!cache.json
|
|
453
499
|
!types.ts
|
|
454
|
-
`,
|
|
500
|
+
`,y=`export declare global {
|
|
455
501
|
interface Window {
|
|
456
502
|
__KOSMO_HYDRATION_BOOL__: boolean;
|
|
457
503
|
__KOSMO_HYDRATION_DATA__: Record<string, unknown> | undefined;
|
|
458
504
|
}
|
|
459
505
|
}
|
|
460
|
-
`,
|
|
506
|
+
`,b=`// stub schemas, specialized generators supposed to overwrite this file
|
|
461
507
|
import type { ValidationSchemas } from "@kosmojs/core/api";
|
|
462
508
|
export type { ValidationSchemas };
|
|
463
509
|
export const validationSchemas: ValidationSchemas = {};
|
|
464
|
-
`,
|
|
465
|
-
//# sourceMappingURL=pkg-
|
|
510
|
+
`,x=({dependencies:e,devDependencies:t},n)=>{let i="${configDir}",a=Object.keys({...e,...t}),o={types:[`vite/client`,...[`node`,`deno`,`bun`].flatMap(e=>a.includes(`@types/${e}`)?[`@types/${e}`]:[])],moduleResolution:`bundler`,module:`ESNext`,target:`ESNext`,strict:!0,exactOptionalPropertyTypes:!0,noImplicitAny:!0,noImplicitThis:!0,noImplicitOverride:!0,noImplicitReturns:!0,noUnusedLocals:!1,noUnusedParameters:!1,allowArbitraryExtensions:!0,allowImportingTsExtensions:!0,allowUnreachableCode:!1,allowUnusedLabels:!1,useUnknownInCatchVariables:!0,noFallthroughCasesInSwitch:!0,noUncheckedSideEffectImports:!0,resolveJsonModule:!0,esModuleInterop:!0,verbatimModuleSyntax:!0,skipLibCheck:!0,noEmit:!0};return n?{include:[`${i}/`,`${i}/../../${r.libDir}/${n}/`,`${i}/../../**/*.d.ts`],compilerOptions:{...o,types:[...o.types],paths:{[`${r.appPrefix}/*`]:[`${i}/../../*`],[`${r.srcPrefix}/*`]:[`${i}/*`],[`${r.libPrefix}/*`]:[`${i}/../../${r.libDir}/${n}/*`]}}}:{include:[`${i}/`],exclude:[`${i}/${r.srcDir}/`],compilerOptions:{...o,paths:{[`${r.appPrefix}/*`]:[`${i}/*`]}}}},S=a(i=>{let{createPath:a,createImportHelpers:d}=o(i),S=async()=>{let{generators:e=[]}=i.config,{dependencies:o={},devDependencies:s={}}=await import(t(i.root,`package.json`),{with:{type:`json`}}).then(e=>e.default);{let t=[],r=[],i=e.flatMap(e=>[`dependencies`,`devDependencies`].flatMap(t=>e[t]?Object.entries(typeof e[t]==`function`?e[t](e.options):e[t]).flatMap(([e,n])=>{let r=l.minVersion(n)?.version;return r?[[e,r,t]]:[]}):[]));for(let[e,n,a]of i){let i=o[e]||s[e],c=i?l.minVersion(i)?.version:void 0;!i||!c?t.push([e,n,a]):l.lt(c,n)&&r.push([e,n,a])}if(t.length){console.error(n([`red`,`italic`],`There are ${t.length} missing dependencies, please consider installing them.`));for(let e of[`dependencies`,`devDependencies`]){let r=t.filter(t=>t[2]===e);r.length&&console.error(`${e}: ${n([`blue`],r.map(([e])=>e).join(` `))}`)}}r.length&&(console.error(n([`yellow`,`italic`],`There are ${r.length} outdated dependencies, please consider updating them:`)),console.error(r.map(([e])=>e).join(` `)),console.error())}{await c(t(i.root,`tsconfig.json`),JSON.stringify({extends:`./${r.libDir}/tsconfig.json`},void 0,2),{},{overwrite:!1}),await c(a.lib(`../tsconfig.json`),JSON.stringify(x({dependencies:o,devDependencies:s}),void 0,2),{}),await c(a.src(`tsconfig.json`),JSON.stringify({extends:`../../${r.libDir}/${i.name}/tsconfig.json`},void 0,2),{},{overwrite:!1});let e=x({dependencies:o,devDependencies:s},i.name),n={},l=new Set(e.compilerOptions.types||[]);for(let{meta:e}of i.config.generators||[]){e.jsx&&(n.jsx=e.jsx),e.jsxImportSource&&(n.jsxImportSource=e.jsxImportSource);for(let t of e.types||[])l.add(t)}await c(a.lib(`tsconfig.json`),JSON.stringify({...e,compilerOptions:{...e.compilerOptions,...n,types:[...l.values()]}},void 0,2),{})}for(let[e,t]of[[`env.d.ts`,_],[`global.d.ts`,y]])await c(a.lib(`../${e}`),t,{});await c(a.lib(`../.gitignore`),v,{},{overwrite:!1});for(let[e,t]of[[`ssg.ts`,`export default [];`]])await c(a.lib(e),t,{})},C=async t=>{let{renderToFile:n}=s({helpers:{...d({origin:`lib`}),...u()},partials:{routeMapperPartial:h}});await n(a.libCore(`routes.ts`),m,{apiRoutes:t.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]),pageRoutes:t.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[])});for(let[e,t]of[[`config.ts`,f],[`types.ts`,g],[`index.ts`,p]])await n(a.libCore(e),t,i);for(let{kind:r,entry:i}of t)r===`apiRoute`&&await n(a.libApi(e(i.file),`schemas.ts`),b,{route:i},{overwrite:!1})};return{start:S,watch:C,build:C}}),C=i({meta:{name:`Core`},dependencies:{"path-to-regexp":d.devDependencies[`path-to-regexp`]},factory:S});export{C as t};
|
|
511
|
+
//# sourceMappingURL=pkg-Bkag325q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkg-Bkag325q.js","names":["l","t","n","o","s","i","r","u","a","e"],"sources":["../../../../generators/core-generator/pkg/index.js"],"sourcesContent":["import{defineGenerator as e,defineGeneratorFactory as t,pathResolver as n,renderFactory as r,renderToFile as i}from\"@kosmojs/lib\";import{dirname as a,resolve as o}from\"node:path\";import{styleText as s}from\"node:util\";import c from\"semver\";import{defaults as l}from\"@kosmojs/core\";import{routeRenderHelpers as u}from\"@kosmojs/core/generators\";var d={type:`module`,private:!0,name:`@kosmojs/core-generator`,version:`0.3.0`,author:`Slee Woo`,license:`MIT`,files:[`pkg/*`],exports:{\".\":{types:`./pkg/index.d.ts`,default:`./pkg/index.js`}},scripts:{build:`wsbuild src/index.ts`},dependencies:{\"@kosmojs/core\":`workspace:^`,\"@kosmojs/lib\":`workspace:^`,semver:`^7.8.5`},devDependencies:{\"@types/semver\":`^7.8.0`,\"path-to-regexp\":`^8.4.2`}},f=`export const base = \"{{config.base}}\";\nexport const apiBase = \"{{config.apiBase}}\";\n`,p=`export * from \"./config\";\nexport * from \"./routes\";\nexport * from \"./types\";\n`,m=`{{> routeMapperPartial}}\n\nimport { base, apiBase } from \"./config\";\n\n// used by backend generators and fetch clients\nexport const apiRouteMap = {\n {{#each apiRoutes}}\n \"{{name}}\": apiRouteMapper<[{{serializeParamsTupleElements .}}]>(\n join(base, apiBase),\n {{serializeApiRoute .}},\n ),\n {{/each}}\n};\n\n// used by frontend generators on Link component\nexport const pageRouteMap = {\n {{#each pageRoutes}}\n \"{{name}}\": pageRouteMapper<[{{serializeParamsTupleElements .}}]>(\n base,\n {{serializePageRoute .}},\n ),\n {{/each}}\n};\n\n{{#if pageRoutes.length}}\nexport type LinkProps =\n {{#each pageRoutes}}\n | [ \"{{name}}\", {{serializeParamsTupleElements .}} ]\n {{/each}};\n{{else}}\nexport type LinkProps = never;\n{{/if}}\n`,h=`import { compile, match } from \"path-to-regexp\";\n\nimport {\n type ApiRouteSerialized,\n type PageRouteSerialized,\n stringifySearchParams,\n type ValidationTarget,\n} from \"@kosmojs/core\";\nimport type { HTTPMethod } from \"@kosmojs/core/api\";\nimport { createHost, join } from \"@kosmojs/core/fetch\";\nimport type { RoutePathMethods } from \"@kosmojs/core/generators\";\n\nexport const apiRouteMapper = <ParamsT extends readonly unknown[]>(\n base: string,\n {\n name,\n pathPattern,\n params,\n numericProperties,\n booleanProperties,\n }: ApiRouteSerialized,\n): RoutePathMethods<ParamsT> & {\n normalizeParams: (path: string) => Record<string, unknown>;\n normalizeSearchParams: (\n searchParams: Record<string, unknown>,\n method: HTTPMethod,\n ) => Record<string, unknown>;\n payloadResolver: <T>(\n payload: Record<ValidationTarget, T> | undefined,\n target: ValidationTarget,\n method: HTTPMethod,\n ) => T | Record<string, unknown> | undefined;\n} => {\n const toPath = compile(pathPattern);\n const pathMatcher = match(join(base, pathPattern));\n\n const maybeNumber = (val: unknown) => {\n if (val === undefined || val === null) {\n return val;\n }\n const n = Number(val);\n return Number.isFinite(n) ? n : val;\n };\n\n const maybeBoolean = (val: unknown) => {\n return [true, false, \"true\", \"false\"].includes(val as never) //\n ? JSON.parse(val as never)\n : val;\n };\n\n const resolveParam = (path: string, param: (typeof params)[number]) => {\n try {\n const match = pathMatcher(path);\n return match ? match.params[param] : undefined;\n } catch (e) {\n return undefined;\n }\n };\n\n const normalizeSearchParams = (\n searchParams: Record<string, unknown>,\n method: HTTPMethod,\n ) => {\n return Object.fromEntries(\n Object.entries(searchParams).map(([k, v]) => {\n if (numericProperties.query?.[method]?.includes(k)) {\n return [\n k,\n Array.isArray(v) ? v.map((e) => maybeNumber(e)) : maybeNumber(v),\n ];\n }\n if (booleanProperties.query?.[method]?.includes(k)) {\n return [\n k,\n Array.isArray(v) ? v.map((e) => maybeBoolean(e)) : maybeBoolean(v),\n ];\n }\n return [k, v];\n }),\n );\n };\n\n const normalizeParams = (path: string) => {\n return params.reduce((map: Record<string, unknown>, param) => {\n const value = resolveParam(path, param);\n if (Array.isArray(value)) {\n map[param] = numericProperties.params.includes(param)\n ? value.map((e) => maybeNumber(e))\n : value;\n } else if (value) {\n map[param] = numericProperties.params.includes(param)\n ? maybeNumber(value)\n : value;\n }\n return map;\n }, {});\n };\n\n const paramsMapper: RoutePathMethods<ParamsT>[\"paramsMapper\"] = (\n input,\n opt,\n ) => {\n return params.reduce<Record<string, unknown>>((map, name, i) => {\n const coerceNumbers = opt?.coerceNumbers\n ? numericProperties.params.includes(name)\n : false;\n if (Array.isArray(input[i])) {\n map[name] = coerceNumbers\n ? input[i].map((v) => maybeNumber(v))\n : input[i].map(String);\n } else if (input[i] !== undefined) {\n map[name] = coerceNumbers ? maybeNumber(input[i]) : String(input[i]);\n }\n return map;\n }, {});\n };\n\n const parametrize: RoutePathMethods<ParamsT>[\"parametrize\"] = (params) => {\n try {\n return toPath(paramsMapper(params) as never);\n } catch (error) {\n console.error(\\`❗ERROR: Failed building path for \\${name}\\`);\n throw error;\n }\n };\n\n const path: RoutePathMethods<ParamsT>[\"path\"] = (params, query, opt) => {\n const path = join(\n opt?.prefix === false\n ? \"/\"\n : typeof opt?.prefix === \"string\"\n ? opt.prefix\n : base,\n parametrize(params),\n );\n return query //\n ? [path, stringifySearchParams(query)].join(\"?\")\n : path;\n };\n\n const href: RoutePathMethods<ParamsT>[\"href\"] = (\n host,\n params,\n query,\n opt,\n ) => {\n return createHost(host) + path(params, query, opt);\n };\n\n const payloadResolver = <T>(\n payload: Record<ValidationTarget, T> | undefined,\n target: ValidationTarget,\n method: HTTPMethod,\n ) => {\n const data = payload?.[target];\n\n if (target === \"query\") {\n return Object.fromEntries(\n Object.entries({ ...data }).map(([k, v]) => {\n return [\n k,\n numericProperties.query[method]?.includes(k)\n ? Array.isArray(v)\n ? v.map((v) => maybeNumber(v))\n : maybeNumber(v)\n : v,\n ];\n }),\n );\n }\n\n if (data instanceof FormData) {\n return [...data].reduce<\n Record<string, FormDataEntryValue | Array<FormDataEntryValue>>\n >((map, [key, val]) => {\n if (key in map) {\n map[key] = [map[key]].flat().concat(val);\n } else {\n map[key] = val;\n }\n return map;\n }, {}) as T;\n }\n\n return data;\n };\n\n return {\n normalizeParams,\n normalizeSearchParams,\n payloadResolver,\n paramsMapper,\n parametrize,\n path,\n href,\n };\n};\n\nexport const pageRouteMapper = <ParamsT extends readonly unknown[]>(\n base: string,\n route: PageRouteSerialized,\n): RoutePathMethods<ParamsT> => {\n const toPath = compile(route.pathPattern);\n\n const paramsMapper: RoutePathMethods<ParamsT>[\"paramsMapper\"] = (params) => {\n return route.params.reduce<Record<string, unknown>>((map, name, i) => {\n if (Array.isArray(params[i])) {\n map[name] = params[i].map(String);\n } else if (params[i] !== undefined) {\n map[name] = String(params[i]);\n }\n return map;\n }, {});\n };\n\n const parametrize: RoutePathMethods<ParamsT>[\"parametrize\"] = (params) => {\n try {\n return toPath(paramsMapper(params) as never);\n } catch (error) {\n console.error(\\`❗ERROR: Failed building path for \\${route.name}\\`);\n throw error;\n }\n };\n\n const path: RoutePathMethods<ParamsT>[\"path\"] = (params, query, opt) => {\n const path = join(\n opt?.prefix === false\n ? \"/\"\n : typeof opt?.prefix === \"string\"\n ? opt.prefix\n : base,\n parametrize(params),\n );\n return query //\n ? [path, stringifySearchParams(query)].join(\"?\")\n : path;\n };\n\n const href: RoutePathMethods<ParamsT>[\"href\"] = (\n host,\n params,\n query,\n opt,\n ) => {\n return createHost(host) + path(params, query, opt);\n };\n\n return {\n paramsMapper,\n parametrize,\n path,\n href,\n };\n};\n`,g=`export type Override<A, B> = Omit<A, keyof B> & B;\n`,_=`declare const KOSMO_PRODUCTION_BUILD: boolean;\n\n/**\n * Enhances base TypeScript types with JSON Schema validation constraints.\n * Allows declaring refined types that carry validation metadata for runtime\n * schema validation while maintaining full TypeScript type safety.\n *\n * Useful for generating validation schemas and ensuring\n * data conforms to specific business rules beyond basic type checking.\n * */\ndeclare type VRefine<\n T extends unknown[] | number | string | object,\n _ extends T extends unknown[]\n ? TArrayOptions\n : T extends number\n ? TNumberOptions\n : T extends string\n ? TStringOptions\n : TObjectOptions,\n> = T;\n\n/**\n * Type definitions inspired by and gently adapted from TypeBox.\n * Original TypeBox created by sinclairzx81: https://github.com/sinclairzx81/typebox\n * TypeBox is licensed under MIT: https://github.com/sinclairzx81/typebox/blob/main/license\n *\n * These types provide JSON Schema compatible type refinements for TypeScript.\n * */\ninterface TSchema {}\n\n// ------------------------------------------------------------------\n// ObjectOptions\n// ------------------------------------------------------------------\ninterface TObjectOptions {\n /**\n * Defines whether additional properties are allowed beyond those explicitly defined in \\`properties\\`.\n */\n additionalProperties?: TSchema | boolean;\n /**\n * The minimum number of properties required in the object.\n */\n minProperties?: number;\n /**\n * The maximum number of properties allowed in the object.\n */\n maxProperties?: number;\n /**\n * Defines conditional requirements for properties.\n */\n dependencies?: Record<string, boolean | TSchema | string[]>;\n /**\n * Specifies properties that *must* be present if a given property is present.\n */\n dependentRequired?: Record<string, string[]>;\n /**\n * Defines schemas that apply if a specific property is present.\n */\n dependentSchemas?: Record<string, TSchema>;\n /**\n * Maps regular expressions to schemas properties matching a pattern must validate against the schema.\n */\n patternProperties?: Record<string, TSchema>;\n /**\n * A schema that all property names within the object must validate against.\n */\n propertyNames?: TSchema;\n}\n\n// ------------------------------------------------------------------\n// ArrayOptions\n// ------------------------------------------------------------------\ninterface TArrayOptions {\n /**\n * The minimum number of items allowed in the array.\n */\n minItems?: number;\n /**\n * The maximum number of items allowed in the array.\n */\n maxItems?: number;\n /**\n * A schema that at least one item in the array must validate against.\n */\n contains?: TSchema;\n /**\n * The minimum number of array items that must validate against the \\`contains\\` schema.\n */\n minContains?: number;\n /**\n * The maximum number of array items that may validate against the \\`contains\\` schema.\n */\n maxContains?: number;\n /**\n * An array of schemas, where each schema in \\`prefixItems\\` validates against items at corresponding positions from the beginning of the array.\n */\n prefixItems?: TSchema[];\n /**\n * If \\`true\\`, all items in the array must be unique.\n */\n uniqueItems?: boolean;\n}\n\n// ------------------------------------------------------------------\n// NumberOptions\n// ------------------------------------------------------------------\ninterface TNumberOptions {\n /**\n * Specifies an exclusive upper limit for the number (number must be less than this value).\n */\n exclusiveMaximum?: number | bigint;\n /**\n * Specifies an exclusive lower limit for the number (number must be greater than this value).\n */\n exclusiveMinimum?: number | bigint;\n /**\n * Specifies an inclusive upper limit for the number (number must be less than or equal to this value).\n */\n maximum?: number | bigint;\n /**\n * Specifies an inclusive lower limit for the number (number must be greater than or equal to this value).\n */\n minimum?: number | bigint;\n /**\n * Specifies that the number must be a multiple of this value.\n */\n multipleOf?: number | bigint;\n}\n\n// ------------------------------------------------------------------\n// StringOptions\n// ------------------------------------------------------------------\ntype TFormat =\n | \"date-time\"\n | \"date\"\n | \"duration\"\n | \"email\"\n | \"hostname\"\n | \"idn-email\"\n | \"idn-hostname\"\n | \"ipv4\"\n | \"ipv6\"\n | \"iri-reference\"\n | \"iri\"\n | \"json-pointer-uri-fragment\"\n | \"json-pointer\"\n | \"json-string\"\n | \"regex\"\n | \"relative-json-pointer\"\n | \"time\"\n | \"uri-reference\"\n | \"uri-template\"\n | \"url\"\n | \"uuid\";\n\ninterface TStringOptions {\n /**\n * Specifies the expected string format.\n *\n * Common values include:\n * - \\`base64\\` – Base64-encoded string.\n * - \\`date-time\\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time format.\n * - \\`date\\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date (YYYY-MM-DD).\n * - \\`duration\\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) duration format.\n * - \\`email\\` – RFC 5321/5322 compliant email address.\n * - \\`hostname\\` – RFC 1034/1035 compliant host name.\n * - \\`idn-email\\` – Internationalized email address.\n * - \\`idn-hostname\\` – Internationalized host name.\n * - \\`ipv4\\` – IPv4 address.\n * - \\`ipv6\\` – IPv6 address.\n * - \\`iri\\` / \\`iri-reference\\` – Internationalized Resource Identifier.\n * - \\`json-pointer\\` / \\`json-pointer-uri-fragment\\` – JSON Pointer format.\n * - \\`json-string\\` – String containing valid JSON.\n * - \\`regex\\` – Regular expression syntax.\n * - \\`relative-json-pointer\\` – Relative JSON Pointer format.\n * - \\`time\\` – [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) time (HH:MM:SS).\n * - \\`uri-reference\\` / \\`uri-template\\` – URI reference or template.\n * - \\`url\\` – Web URL format.\n * - \\`uuid\\` – RFC 4122 UUID string.\n *\n * May also be a custom format string.\n */\n format?: TFormat;\n /**\n * Specifies the minimum number of characters allowed in the string.\n * Must be a non-negative integer.\n */\n minLength?: number;\n /**\n * Specifies the maximum number of characters allowed in the string.\n * Must be a non-negative integer.\n */\n maxLength?: number;\n /**\n * Specifies a regular expression pattern that the string value must match.\n * Can be provided as a string (ECMA-262 regex syntax) or a \\`RegExp\\` object.\n */\n pattern?: string | RegExp;\n}\n`,v=`# Ignore all files\n*\n\n# But don't ignore directories (so Git can traverse them)\n!*/\n\n# And don't ignore these files at any depth\n!cache.json\n!types.ts\n`,y=`export declare global {\n interface Window {\n __KOSMO_HYDRATION_BOOL__: boolean;\n __KOSMO_HYDRATION_DATA__: Record<string, unknown> | undefined;\n }\n}\n`,b=`// stub schemas, specialized generators supposed to overwrite this file\nimport type { ValidationSchemas } from \"@kosmojs/core/api\";\nexport type { ValidationSchemas };\nexport const validationSchemas: ValidationSchemas = {};\n`,x=({dependencies:e,devDependencies:t},n)=>{let r=\"${configDir}\",i=Object.keys({...e,...t}),a={types:[`vite/client`,...[`node`,`deno`,`bun`].flatMap(e=>i.includes(`@types/${e}`)?[`@types/${e}`]:[])],moduleResolution:`bundler`,module:`ESNext`,target:`ESNext`,strict:!0,exactOptionalPropertyTypes:!0,noImplicitAny:!0,noImplicitThis:!0,noImplicitOverride:!0,noImplicitReturns:!0,noUnusedLocals:!1,noUnusedParameters:!1,allowArbitraryExtensions:!0,allowImportingTsExtensions:!0,allowUnreachableCode:!1,allowUnusedLabels:!1,useUnknownInCatchVariables:!0,noFallthroughCasesInSwitch:!0,noUncheckedSideEffectImports:!0,resolveJsonModule:!0,esModuleInterop:!0,verbatimModuleSyntax:!0,skipLibCheck:!0,noEmit:!0};return n?{include:[`${r}/`,`${r}/../../${l.libDir}/${n}/`,`${r}/../../**/*.d.ts`],compilerOptions:{...a,types:[...a.types],paths:{[`${l.appPrefix}/*`]:[`${r}/../../*`],[`${l.srcPrefix}/*`]:[`${r}/*`],[`${l.libPrefix}/*`]:[`${r}/../../${l.libDir}/${n}/*`]}}}:{include:[`${r}/`],exclude:[`${r}/${l.srcDir}/`],compilerOptions:{...a,paths:{[`${l.appPrefix}/*`]:[`${r}/*`]}}}},S=t(e=>{let{createPath:t,createImportHelpers:d}=n(e),S=async()=>{let{generators:n=[]}=e.config,{dependencies:r={},devDependencies:a={}}=await import(o(e.root,`package.json`),{with:{type:`json`}}).then(e=>e.default);{let e=[],t=[],i=n.flatMap(e=>[`dependencies`,`devDependencies`].flatMap(t=>e[t]?Object.entries(typeof e[t]==`function`?e[t](e.options):e[t]).flatMap(([e,n])=>{let r=c.minVersion(n)?.version;return r?[[e,r,t]]:[]}):[]));for(let[n,o,s]of i){let i=r[n]||a[n],l=i?c.minVersion(i)?.version:void 0;!i||!l?e.push([n,o,s]):c.lt(l,o)&&t.push([n,o,s])}if(e.length){console.error(s([`red`,`italic`],`There are ${e.length} missing dependencies, please consider installing them.`));for(let t of[`dependencies`,`devDependencies`]){let n=e.filter(e=>e[2]===t);n.length&&console.error(`${t}: ${s([`blue`],n.map(([e])=>e).join(` `))}`)}}t.length&&(console.error(s([`yellow`,`italic`],`There are ${t.length} outdated dependencies, please consider updating them:`)),console.error(t.map(([e])=>e).join(` `)),console.error())}{await i(o(e.root,`tsconfig.json`),JSON.stringify({extends:`./${l.libDir}/tsconfig.json`},void 0,2),{},{overwrite:!1}),await i(t.lib(`../tsconfig.json`),JSON.stringify(x({dependencies:r,devDependencies:a}),void 0,2),{}),await i(t.src(`tsconfig.json`),JSON.stringify({extends:`../../${l.libDir}/${e.name}/tsconfig.json`},void 0,2),{},{overwrite:!1});let n=x({dependencies:r,devDependencies:a},e.name),s={},c=new Set(n.compilerOptions.types||[]);for(let{meta:t}of e.config.generators||[]){t.jsx&&(s.jsx=t.jsx),t.jsxImportSource&&(s.jsxImportSource=t.jsxImportSource);for(let e of t.types||[])c.add(e)}await i(t.lib(`tsconfig.json`),JSON.stringify({...n,compilerOptions:{...n.compilerOptions,...s,types:[...c.values()]}},void 0,2),{})}for(let[e,n]of[[`env.d.ts`,_],[`global.d.ts`,y]])await i(t.lib(`../${e}`),n,{});await i(t.lib(`../.gitignore`),v,{},{overwrite:!1});for(let[e,n]of[[`ssg.ts`,`export default [];`]])await i(t.lib(e),n,{})},C=async n=>{let{renderToFile:i}=r({helpers:{...d({origin:`lib`}),...u()},partials:{routeMapperPartial:h}});await i(t.libCore(`routes.ts`),m,{apiRoutes:n.flatMap(({kind:e,entry:t})=>e===`apiRoute`?[t]:[]),pageRoutes:n.flatMap(({kind:e,entry:t})=>e===`pageRoute`?[t]:[])});for(let[n,r]of[[`config.ts`,f],[`types.ts`,g],[`index.ts`,p]])await i(t.libCore(n),r,e);for(let{kind:e,entry:r}of n)e===`apiRoute`&&await i(t.libApi(a(r.file),`schemas.ts`),b,{route:r},{overwrite:!1})};return{start:S,watch:C,build:C}}),C=e({meta:{name:`Core`},dependencies:{\"path-to-regexp\":d.devDependencies[`path-to-regexp`]},factory:S});export{C as default};\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
|
package/pkg/chassis.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./assets/pkg-
|
|
1
|
+
import{t as e}from"./assets/pkg-Bkag325q.js";import{createRequire as t}from"node:module";import{dirname as n,join as r,resolve as i}from"node:path";import{styleText as a}from"node:util";import{defaults as o}from"@kosmojs/core";import{mergeConfigs as s,pathExists as c,pathResolver as l,routesFactory as u,spinnerFactory as d,vitePlugins as f}from"@kosmojs/lib";import p from"crc/crc32";import{build as m,createServer as h}from"vite";import{mkdir as g,readFile as _,writeFile as v}from"node:fs/promises";import y from"node:http";import b from"node:net";import{readFileSync as x}from"node:fs";var S=JSON.parse(x(t(import.meta.url).resolve(`@kosmojs/dev/package.json`),`utf-8`)),C=(e,t,r)=>{let a=l(t).createPath.libApi(n(e.file),`cache.json`),o=async n=>{if(!n?.hash||!n.typeDeclarations||!n.referencedFiles)return;let a=await w(e.fileFullpath,{...r});if(T(n.hash,a)){for(let[e,r]of Object.entries(n.referencedFiles))if(!T(r,await w(i(t.root,e))))return;return n}};return{async get(e){if(await c(a))try{let t=JSON.parse(await _(a,`utf8`));return e?.validate?o(t):t}catch{}},async set(i){let o=await w(e.fileFullpath,{...r}),s={};for(let e of i.referencedFiles)s[e.replace(`${t.root}/`,``)]=await w(e);let c={...i,hash:o,referencedFiles:s};return await g(n(a),{recursive:!0}),await v(a,JSON.stringify(c,null,2),`utf8`),c}}},w=async(e,t)=>{let n;try{n=await _(e,`utf8`)}catch{return 0}return n?p(JSON.stringify({...t,[S.cacheVersion]:n})):0},T=(e,t)=>e===t,E=async e=>{let{devPort:t,command:n}=e;for(let t of e.sourceFolders)for(let e of O(t)){if(!e.meta?.name||typeof e.factory!=`function`)throw Error(`${t.name}: Unrecognized generator - must be created via defineGenerator()`);try{await e.factory(t).start?.()}catch(n){throw console.error(a(`red`,`${t.name}: ${e.meta.name} generator failed to initialize`)),n}}if(n===`build`){for(let t of e.sourceFolders){let{createPath:e}=l(t),r=[];{let{resolvers:e}=await u(t,C),n=d(`${t.name}: resolving routes`);for(let{name:t,handler:i}of e.values())n.append(`[ ${r.length+1} of ${e.size} ] ${t}`),r.push(await i());n.succeed(`ready ✨`)}let i=O(t);for(let e of i)await e.factory(t).build?.(r);let a=[f.tsconfigPaths(t),f.nodePrefix()];await m(s(t.config,...i.map(({factory:e})=>e(t).config?.({kind:`client`,command:n})),{root:e.src(),cacheDir:D(t,n,`client`),plugins:a,build:{outDir:e.distDir(`client`),manifest:!0,emptyOutDir:!0}}));let o=i.find(e=>e.meta.slot===`backend`);if(o){let r=e.distDir(`api`);await m(s(o.options,o.factory(t).config?.({kind:`backend`,command:n}),{base:`./`,root:e.src(),appType:`custom`,plugins:a,resolve:{conditions:[`node`]},build:{ssr:!0,target:`esnext`,sourcemap:!0,emptyOutDir:!0,rolldownOptions:{input:[e.api(`app.ts`),e.api(`server.ts`)],output:{dir:r,format:`esm`}}},cacheDir:D(t,n,`backend`)}))}for(let e of i)await e.factory(t).postBuild?.(r)}return async()=>{}}let i=[],c=[],p={};for(let t of e.sourceFolders)p[t.name]=await k(t);let g=await M(t);for(let t of e.sourceFolders){let{createPath:e}=l(t),u=j(t),d=O(t),m=[f.tsconfigPaths(t),f.nodePrefix()];{let r=await h(s(t.config,...d.map(({factory:e})=>e(t).config?.({kind:`client`,command:n})),{root:e.src(),cacheDir:D(t,n,`client`),plugins:m,server:{port:g++,middlewareMode:!0,hmr:{port:g++}}}));for(let[e,n]of Object.entries(p[t.name]))r.watcher.on(e,n);i.push([[t.config.base],()=>u.base,()=>r.middlewares]),c.push(r.close)}let _=d.find(e=>e.meta.slot===`backend`);if(_){let l=await h(s(_.options,_.factory(t).config?.({kind:`backend`,command:n}),{root:e.src(),appType:`custom`,cacheDir:D(t,n,`backend`),plugins:m,server:{port:g++,middlewareMode:!0,hmr:{port:g++}},resolve:{conditions:[`node`]},environments:{api:{resolve:{conditions:[`node`]}}}})),d=l.environments.api,f=async()=>(d.runner.clearCache(),d.runner.import(r(o.apiDir,`dev.ts`)).then(e=>e.default)),v=await f();for(let[e,n]of Object.entries(p[t.name]))l.watcher.on(e,async e=>{if(d.moduleGraph.getModulesByFile(e)?.size)try{await n(e),await v?.teardownHandler?.(),v=await f()}catch(e){console.error(a(`red`,`${t.name}: api reload failed - keeping previous api program`)),console.error(e)}});i.push([[t.config.base,t.config.apiBase],()=>v.requestMatcher||u.api,()=>v.requestHandler()]),c.push(l.close)}}let _=([e])=>{let[t,n]=e,r=t.split(`/`).filter(Boolean).length;return n?r+5:r},v=i.sort((e,t)=>_(t)-_(e)),b=y.createServer((e,t)=>{for(let[,n,r]of v){let[i,a]=[n(),r()];if(i(e)){a(e,t);return}}t.writeHead(404,{"Content-Type":`text/html`}),t.end(`<h1>404: Not Found</h1>`)});return b.on(`error`,e=>{console.error(a(`red`,`Failed to start dev server on port ${t}`)),console.error(e.message),process.exit(1)}),b.listen(t),c.push(async()=>{b.close()}),async()=>{for(let e of c)await e();await new Promise(e=>setTimeout(e,100))}},D=({root:e,name:t},n,r)=>i(e,`var/.vite/${t}/${n}/${r}`),O=t=>{let{generators:n=[]}=t.config,r={},i=[];for(let e of n)e.meta.slot?r[e.meta.slot]=e:i.push(e);return[e(),...r.backend?[r.backend]:[],...r.fetch&&r.backend?[r.fetch]:[],...i,...r.ssr?[r.ssr]:[],...r.ssg?[r.ssg]:[]]},k=async e=>{let{resolvers:t,resolversFactory:n}=await u(e,C),{createPath:r}=l(e),i=[];for(let t of O(e)){let n=t.factory(e);i.push({name:t.meta.name,factory:n})}let o=new Map,s=async t=>{let n=Array.from(o.values());for(let{name:r,factory:o}of i)try{await o.watch?.(n,t)}catch(n){console.error(a(`red`,`${e.name}: ${r} generator failed`)),t&&console.error(t),console.error(n)}},c=async n=>{let i=t.get(n);if(i)try{let e=await i.handler(n);return o.set(e.entry.fileFullpath,e),e}catch(t){let i=n.replace(`${r.api()}/`,``);console.error(a(`red`,`${e.name}: ${i} route resolution failed`)),console.error(t);return}};{let n=d(`${e.name}: resolving routes`);for(let{name:e,handler:r}of t.values()){n.append(`[ ${o.size+1} of ${t.size} ] ${e}`);let i=await r();o.set(i.entry.fileFullpath,i)}n.succeed(`ready ✨`)}return await s(),{async add(e){let[r]=n([e]).values();r&&(t.set(e,r),await c(e)&&await s({kind:`create`,file:e}))},async change(e){if(o.has(e))await c(e);else{let t=o.values().flatMap(({kind:t,entry:n})=>t===`apiRoute`&&n.referencedFiles.includes(e)?[n]:[]);for(let e of t)await c(e.fileFullpath)}await s({kind:`update`,file:e})},async unlink(e){t.delete(e),o.delete(e),await s({kind:`delete`,file:e})}}},A=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`).replace(/\/+$/,``),j=({config:e})=>{let t=RegExp(`^${A(e.base)}(?=$|/)`),n=RegExp(`^${A(r(e.base,e.apiBase))}(?=$|/)`);return{base(e){return!n.test(e.url)&&t.test(e.url)},api(e){return n.test(e.url)}}},M=async e=>{let t=0,n=0;for(let r of[3,2,1,``])if(t=Number(`${r}${e}`)+100,n=t+100,n<65e3)break;if(n>=65e3)throw Error(`the devPort in package.json should be less than 64000`);let r=n-t+1,i=Math.floor(Math.random()*r),a=await Array.from({length:r},(e,n)=>t+(i+n)%r).reduce(async(e,t)=>await e||(await N(t)?t:void 0),Promise.resolve(void 0));if(!a)throw Error(`No free ports found in range ${t}-${n}`);return a},N=e=>new Promise(t=>{let n=b.createServer();n.once(`error`,()=>t(!1)),n.once(`listening`,()=>{n.close(),t(!0)}),n.listen(e,`127.0.0.1`)});export{E as default};
|
|
2
2
|
//# sourceMappingURL=chassis.js.map
|
package/pkg/chassis.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chassis.js","names":[],"sources":["../src/cache.ts","../src/chassis.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, resolve } from \"node:path\";\n\nimport crc from \"crc/crc32\";\n\nimport type {\n RouteResolverCache,\n RouteResolverCacheFactory,\n} from \"@kosmojs/core\";\nimport { pathExists, pathResolver } from \"@kosmojs/lib\";\n\n/**\n * Read the installed package.json at runtime to get the actual version.\n * A static `import ... with { type: \"json\" }` would be inlined by the\n * bundler with the pre-bump version, defeating the point.\n * */\nconst self = JSON.parse(\n readFileSync(\n createRequire(import.meta.url).resolve(\"@kosmojs/dev/package.json\"),\n \"utf-8\",\n ),\n);\n\nexport const cacheFactory: RouteResolverCacheFactory = (\n route,\n sourceFolder,\n extraContext,\n) => {\n const cacheFile = pathResolver(sourceFolder).createPath.libApi(\n dirname(route.file),\n \"cache.json\",\n );\n\n const validateCache = async (cache: RouteResolverCache) => {\n if (!cache?.hash) {\n return;\n }\n\n if (!cache.typeDeclarations || !cache.referencedFiles) {\n // incomplete cache\n return;\n }\n\n const hash = await generateFileHash(route.fileFullpath, {\n ...extraContext,\n });\n\n if (!identicalHashSum(cache.hash, hash)) {\n // route itself updated\n return;\n }\n\n for (const [file, hash] of Object.entries(cache.referencedFiles)) {\n if (\n !identicalHashSum(\n hash,\n await generateFileHash(resolve(sourceFolder.root, file)),\n )\n ) {\n // some referenced file updated\n return;\n }\n }\n\n return cache;\n };\n\n return {\n async get(opt?: { validate?: boolean }) {\n if (await pathExists(cacheFile)) {\n try {\n const cache = JSON.parse(await readFile(cacheFile, \"utf8\"));\n return opt?.validate //\n ? validateCache(cache)\n : cache;\n } catch (_e) {}\n }\n return undefined;\n },\n\n async set(cache) {\n const hash = await generateFileHash(route.fileFullpath, {\n ...extraContext,\n });\n\n const referencedFiles: Record<string, number> = {};\n\n for (const file of cache.referencedFiles) {\n referencedFiles[\n // Strip project root to ensure cached paths are relative\n // and portable across environments (CI, local, etc.)\n file.replace(`${sourceFolder.root}/`, \"\")\n ] = await generateFileHash(file);\n }\n\n const value = { ...cache, hash, referencedFiles };\n\n await mkdir(dirname(cacheFile), { recursive: true });\n await writeFile(cacheFile, JSON.stringify(value, null, 2), \"utf8\");\n\n return value;\n },\n };\n};\n\nconst generateFileHash = async (\n file: string,\n extraContext?: object,\n): Promise<number> => {\n let fileContent: string | undefined;\n try {\n fileContent = await readFile(file, \"utf8\");\n } catch (_e) {\n // file could be deleted since last build\n return 0;\n }\n return fileContent\n ? crc(\n JSON.stringify({\n ...extraContext,\n [self.cacheVersion]: fileContent,\n }),\n )\n : 0;\n};\n\n// return true if sums are identical\nconst identicalHashSum = (a: number, b: number) => {\n return a === b;\n};\n","import http, { type IncomingMessage, type ServerResponse } from \"node:http\";\nimport net from \"node:net\";\nimport { join, resolve } from \"node:path\";\nimport { styleText } from \"node:util\";\n\nimport { build, createServer, type RunnableDevEnvironment } from \"vite\";\n\nimport {\n defaults,\n type GeneratorFactory,\n type GeneratorMeta,\n type GeneratorSignature,\n type ProjectSettings,\n type ResolvedEntry,\n type SourceFolder,\n type WatcherEvent,\n} from \"@kosmojs/core\";\nimport type { DevSetup } from \"@kosmojs/core/api\";\nimport {\n mergeConfigs,\n pathResolver,\n routesFactory,\n spinnerFactory,\n vitePlugins,\n} from \"@kosmojs/lib\";\n\nimport coreGenerator from \"@kosmojs/core-generator\";\n\nimport { cacheFactory } from \"./cache\";\n\nexport default async (\n projectSettings: ProjectSettings,\n): Promise<() => Promise<void>> => {\n const { devPort, command } = projectSettings;\n\n // NOTE: initialize generators before anything else, regardless command\n for (const sourceFolder of projectSettings.sourceFolders) {\n for (const generator of folderGenerators(sourceFolder)) {\n if (!generator.meta?.name || typeof generator.factory !== \"function\") {\n throw new Error(\n `${sourceFolder.name}: Unrecognized generator - must be created via defineGenerator()`,\n );\n }\n\n try {\n await generator.factory(sourceFolder).start?.();\n } catch (error) {\n console.error(\n styleText(\n \"red\",\n `${sourceFolder.name}: ${generator.meta.name} generator failed to initialize`,\n ),\n );\n throw error;\n }\n }\n }\n\n if (command === \"build\") {\n for (const sourceFolder of projectSettings.sourceFolders) {\n const { createPath } = pathResolver(sourceFolder);\n\n const resolvedRoutes = [];\n\n {\n const { resolvers } = await routesFactory(sourceFolder, cacheFactory);\n\n const spinner = spinnerFactory(\n `${sourceFolder.name}: resolving routes`,\n );\n\n for (const { name, handler } of resolvers.values()) {\n spinner.append(\n `[ ${resolvedRoutes.length + 1} of ${resolvers.size} ] ${name}`,\n );\n resolvedRoutes.push(await handler());\n }\n\n spinner.succeed(\"ready ✨\");\n }\n\n const generators = folderGenerators(sourceFolder);\n\n for (const generator of generators) {\n await generator.factory(sourceFolder).build?.(resolvedRoutes);\n }\n\n const plugins = [\n vitePlugins.tsconfigPaths(sourceFolder),\n vitePlugins.nodePrefix(),\n ];\n\n // INFO: === build client ===\n await build(\n mergeConfigs(\n // user-provided config - lowest priority\n sourceFolder.config,\n // generators configs - higher priority\n ...generators.map(({ factory }) => {\n return factory(sourceFolder).config?.({ kind: \"client\", command });\n }),\n // main config - highest priority\n {\n // base provided by sourceFolder.config\n root: createPath.src(),\n cacheDir: cacheDir(sourceFolder, command, \"client\"),\n plugins,\n build: {\n outDir: createPath.distDir(\"client\"),\n manifest: true,\n emptyOutDir: true,\n },\n },\n ),\n );\n\n const apiGenerator = generators.find((e) => e.meta.slot === \"backend\");\n\n // INFO: === build backend ===\n if (apiGenerator) {\n const dir = createPath.distDir(\"api\");\n\n // NOTE: sourceFolder.config is client-specific config - not using for backend!\n // To provide backend-specific config pass it as api generator options.\n await build(\n mergeConfigs(\n // user-provided config - lowest priority\n apiGenerator.options,\n // generator config - higher priority\n apiGenerator\n .factory(sourceFolder)\n .config?.({ kind: \"backend\", command }),\n // main config - highest priority\n {\n base: \"./\",\n root: createPath.src(),\n appType: \"custom\",\n plugins,\n resolve: {\n conditions: [\"node\"],\n },\n build: {\n ssr: true,\n target: \"esnext\",\n sourcemap: true,\n emptyOutDir: true,\n rolldownOptions: {\n input: [\n createPath.api(\"app.ts\"),\n createPath.api(\"server.ts\"),\n ],\n output: {\n dir,\n format: \"esm\",\n },\n },\n },\n cacheDir: cacheDir(sourceFolder, command, \"backend\"),\n },\n ),\n );\n }\n\n for (const generator of generators) {\n await generator.factory(sourceFolder).postBuild?.(resolvedRoutes);\n }\n }\n\n return async () => {};\n }\n\n type RequestMatcher = (req: IncomingMessage) => boolean;\n type RequestHandler = (req: IncomingMessage, res: ServerResponse) => void;\n\n const requestHandlers: Array<\n [\n segments: [base: string, api?: string],\n matcherFactory: () => RequestMatcher,\n handlerFactory: () => RequestHandler,\n ]\n > = [];\n\n const teardownHandlers: Array<() => Promise<unknown>> = [];\n\n const eventMap: Record<string, Awaited<ReturnType<typeof eventFactory>>> = {};\n\n // WARN: call this before starting any server!\n for (const sourceFolder of projectSettings.sourceFolders) {\n eventMap[sourceFolder.name] = await eventFactory(sourceFolder);\n }\n\n let port = await findFreePort(devPort);\n\n for (const sourceFolder of projectSettings.sourceFolders) {\n const { createPath } = pathResolver(sourceFolder);\n\n const requestMatchers = matchersFactory(sourceFolder);\n\n const generators = folderGenerators(sourceFolder);\n\n const plugins = [\n vitePlugins.tsconfigPaths(sourceFolder),\n vitePlugins.nodePrefix(),\n ];\n\n // INFO: === start client server ===\n {\n const viteServer = await createServer(\n mergeConfigs(\n // user-provided config - lowest priority\n sourceFolder.config,\n // generators configs - higher priority\n ...generators.map(({ factory }) => {\n return factory(sourceFolder).config?.({ kind: \"client\", command });\n }),\n // main config - highest priority\n {\n // base provided by sourceFolder.config\n root: createPath.src(),\n cacheDir: cacheDir(sourceFolder, command, \"client\"),\n plugins,\n server: {\n port: port++,\n middlewareMode: true,\n hmr: { port: port++ },\n },\n },\n ),\n );\n\n for (const [evt, handler] of Object.entries(\n eventMap[sourceFolder.name],\n )) {\n viteServer.watcher.on(evt, handler);\n }\n\n requestHandlers.push([\n [sourceFolder.config.base],\n () => requestMatchers.base,\n () => viteServer.middlewares,\n ]);\n\n teardownHandlers.push(viteServer.close);\n }\n\n const apiGenerator = generators.find((e) => e.meta.slot === \"backend\");\n\n // INFO: === start backend server ===\n if (apiGenerator) {\n // NOTE: sourceFolder.config is client-specific config - not using for backend!\n // To provide backend-specific config pass it as api generator options.\n const viteServer = await createServer(\n mergeConfigs(\n // user-provided config - lowest priority\n apiGenerator.options,\n // generator config - higher priority\n apiGenerator\n .factory(sourceFolder)\n .config?.({ kind: \"backend\", command }),\n // main config - highest priority\n {\n root: createPath.src(),\n appType: \"custom\",\n cacheDir: cacheDir(sourceFolder, command, \"backend\"),\n plugins,\n server: {\n port: port++,\n middlewareMode: true,\n hmr: { port: port++ },\n },\n resolve: {\n conditions: [\"node\"],\n },\n environments: {\n api: {\n resolve: {\n conditions: [\"node\"],\n },\n },\n },\n },\n ),\n );\n\n const env = viteServer.environments.api as RunnableDevEnvironment;\n\n const loadDevSetup = async () => {\n env.runner.clearCache();\n return env.runner\n .import<{ default: DevSetup }>(join(defaults.apiDir, \"dev.ts\"))\n .then((e) => e.default);\n };\n\n let devSetup = await loadDevSetup();\n\n for (const [evt, handler] of Object.entries(\n eventMap[sourceFolder.name],\n )) {\n viteServer.watcher.on(evt, async (file) => {\n const mods = env.moduleGraph.getModulesByFile(file);\n if (mods?.size) {\n await handler(file);\n await devSetup?.teardownHandler?.();\n devSetup = await loadDevSetup();\n }\n });\n }\n\n requestHandlers.push([\n [sourceFolder.config.base, sourceFolder.config.apiBase],\n () => devSetup.requestMatcher || requestMatchers.api,\n () => devSetup.requestHandler(),\n ]);\n\n teardownHandlers.push(viteServer.close);\n }\n }\n\n /**\n * Sorting is essential to ensure more specific paths are matched before broader ones.\n *\n * Given source folders:\n * - main app: baseurl=/ apiurl=/api\n * - admin app: baseurl=/admin apiurl=/admin/api\n *\n * Correct sort order:\n * 1. /admin/api — most specific\n * 2. /api\n * 3. /admin\n * 4. /\n * */\n const requestHandlerWeight = ([\n segments,\n ]: (typeof requestHandlers)[number]) => {\n const [base, api] = segments;\n /**\n * set weight to number of non-empty segments:\n * \"/\" weight is 0\n * \"/admin\" weight is 1\n * */\n const weight = base.split(\"/\").filter(Boolean).length;\n return api ? weight + 5 : weight;\n };\n\n const handlers = requestHandlers.sort(\n (a, b) => requestHandlerWeight(b) - requestHandlerWeight(a),\n );\n\n const httpServer = http.createServer((req, res) => {\n for (const [, matcherFactory, handlerFactory] of handlers) {\n // should be called on every request\n const [matcher, handler] = [matcherFactory(), handlerFactory()];\n if (matcher(req)) {\n handler(req, res);\n return;\n }\n }\n res.writeHead(404, { \"Content-Type\": \"text/html\" });\n res.end(\"<h1>404: Not Found</h1>\");\n });\n\n httpServer.on(\"error\", (error) => {\n console.error(\n styleText(\"red\", `Failed to start dev server on port ${devPort}`),\n );\n console.error(error.message);\n process.exit(1);\n });\n\n httpServer.listen(devPort);\n\n teardownHandlers.push(async () => {\n httpServer.close();\n });\n\n return async () => {\n for (const handler of teardownHandlers) {\n await handler();\n }\n // Let chokidar fully release file handles\n await new Promise((resolve) => setTimeout(resolve, 100));\n };\n};\n\nconst cacheDir = (\n { root, name }: SourceFolder,\n command: ProjectSettings[\"command\"],\n mode: \"client\" | \"backend\",\n) => {\n return resolve(root, `var/.vite/${name}/${command}/${mode}`);\n};\n\nconst folderGenerators = (\n sourceFolder: SourceFolder,\n): Array<GeneratorSignature> => {\n const { generators = [] } = sourceFolder.config;\n\n const baseGenerators: Partial<\n Record<NonNullable<GeneratorMeta[\"slot\"]>, GeneratorSignature>\n > = {};\n\n const userGenerators: Array<GeneratorSignature> = [];\n\n for (const generator of generators) {\n if (generator.meta.slot) {\n baseGenerators[generator.meta.slot] = generator;\n } else {\n userGenerators.push(generator);\n }\n }\n\n return [\n // core generator should run first\n coreGenerator(),\n // then backend generator\n ...(baseGenerators.backend ? [baseGenerators.backend] : []),\n // then fetch generator, only if backend generator also enabled\n ...(baseGenerators.fetch && baseGenerators.backend\n ? [baseGenerators.fetch]\n : []),\n // then user generators in the order they were added\n ...userGenerators,\n // ssr generator should run after user generators\n ...(baseGenerators.ssr ? [baseGenerators.ssr] : []),\n // ssg generator should run after ssr generator\n ...(baseGenerators.ssg ? [baseGenerators.ssg] : []),\n ];\n};\n\nconst eventFactory = async (\n sourceFolder: SourceFolder,\n): Promise<\n Record<\"add\" | \"change\" | \"unlink\", (f: string) => Promise<void>>\n> => {\n const { resolvers, resolversFactory } = await routesFactory(\n sourceFolder,\n cacheFactory,\n );\n\n const { createPath } = pathResolver(sourceFolder);\n\n const generators: Array<{\n name: string | undefined;\n factory: GeneratorFactory;\n }> = [];\n\n for (const generator of folderGenerators(sourceFolder)) {\n const factory = generator.factory(sourceFolder);\n generators.push({ name: generator.meta.name, factory });\n }\n\n const resolvedRoutes = new Map<\n string, // fileFullpath\n ResolvedEntry\n >();\n\n const runGenerators = async (event?: WatcherEvent) => {\n /**\n * Watch handlers receive the full list of entries\n * and should process only those whose source file or dependencies were updated.\n * */\n const entries = Array.from(resolvedRoutes.values());\n\n for (const { name, factory } of generators) {\n try {\n await factory.watch?.(entries, event);\n } catch (error) {\n console.error(\n styleText(\"red\", `${sourceFolder.name}: ${name} generator failed`),\n );\n if (event) {\n console.error(event);\n }\n console.error(error);\n }\n }\n };\n\n const updateResolvedEntry = async (file: string) => {\n const resolver = resolvers.get(file);\n\n if (!resolver) {\n return;\n }\n\n try {\n const resolvedEntry = await resolver.handler(file);\n resolvedRoutes.set(resolvedEntry.entry.fileFullpath, resolvedEntry);\n return resolvedEntry;\n } catch (error) {\n const route = file.replace(`${createPath.api()}/`, \"\");\n console.error(\n styleText(\n \"red\",\n `${sourceFolder.name}: ${route} route resolution failed`,\n ),\n );\n console.error(error);\n return;\n }\n };\n\n {\n const spinner = spinnerFactory(`${sourceFolder.name}: resolving routes`);\n for (const { name, handler } of resolvers.values()) {\n spinner.append(\n `[ ${resolvedRoutes.size + 1} of ${resolvers.size} ] ${name}`,\n );\n const route = await handler();\n resolvedRoutes.set(route.entry.fileFullpath, route);\n }\n spinner.succeed(\"ready ✨\");\n }\n\n // NOTE: call only after routes resolved\n await runGenerators();\n\n return {\n async add(file: string) {\n const [resolver] = resolversFactory([file]).values();\n\n if (!resolver) {\n return;\n }\n\n resolvers.set(file, resolver);\n\n // call only after `resolvers.set(file)`\n const resolvedEntry = await updateResolvedEntry(file);\n\n if (resolvedEntry) {\n await runGenerators({ kind: \"create\", file });\n }\n },\n\n async change(file: string) {\n if (resolvedRoutes.has(file)) {\n // route updated\n await updateResolvedEntry(file);\n } else {\n // updating entries that are referencing updated file\n const relatedRoutes = resolvedRoutes\n .values()\n .flatMap(({ kind, entry }) => {\n return kind === \"apiRoute\"\n ? entry.referencedFiles.includes(file)\n ? [entry]\n : []\n : [];\n });\n\n for (const route of relatedRoutes) {\n await updateResolvedEntry(route.fileFullpath);\n }\n }\n\n await runGenerators({ kind: \"update\", file });\n },\n\n async unlink(file) {\n // route deleted\n resolvers.delete(file);\n resolvedRoutes.delete(file);\n await runGenerators({ kind: \"delete\", file });\n },\n };\n};\n\nconst normalizeRegex = (s: string) => {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\/+$/, \"\");\n};\n\nconst matchersFactory: (\n sourceFolder: SourceFolder,\n) => Record<\"base\" | \"api\", (req: IncomingMessage) => boolean> = ({\n config,\n}) => {\n const basePattern = new RegExp(`^${normalizeRegex(config.base)}(?=$|/)`);\n const apiPattern = new RegExp(\n `^${normalizeRegex(join(config.base, config.apiBase))}(?=$|/)`,\n );\n return {\n base(req) {\n return apiPattern.test(req.url as string)\n ? false\n : basePattern.test(req.url as string);\n },\n api(req) {\n return apiPattern.test(req.url as string);\n },\n };\n};\n\nconst findFreePort = async (devPort: number): Promise<number> => {\n let minPort = 0;\n let maxPort = 0;\n\n for (const n of [3, 2, 1, \"\"]) {\n minPort = Number(`${n}${devPort}`) + 100;\n maxPort = minPort + 100;\n if (maxPort < 65000) {\n break;\n }\n }\n\n if (maxPort >= 65000) {\n throw new Error(\"the devPort in package.json should be less than 64000\");\n }\n\n const range = maxPort - minPort + 1;\n const startOffset = Math.floor(Math.random() * range);\n\n const ports = Array.from({ length: range }, (_, i) => {\n return minPort + ((startOffset + i) % range);\n });\n\n const freePort = await ports.reduce(\n async (prevPromise, port) => {\n const freePort = await prevPromise;\n if (freePort) {\n return freePort;\n }\n const isFree = await isPortFree(port);\n return isFree ? port : undefined;\n },\n Promise.resolve(undefined as number | undefined),\n );\n\n if (!freePort) {\n throw new Error(`No free ports found in range ${minPort}-${maxPort}`);\n }\n\n return freePort;\n};\n\nconst isPortFree = (port: number): Promise<boolean> => {\n return new Promise((resolve) => {\n const server = net.createServer();\n\n server.once(\"error\", () => resolve(false));\n\n server.once(\"listening\", () => {\n server.close();\n resolve(true);\n });\n\n server.listen(port, \"127.0.0.1\");\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chassis.js","names":[],"sources":["../src/cache.ts","../src/chassis.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, resolve } from \"node:path\";\n\nimport crc from \"crc/crc32\";\n\nimport type {\n RouteResolverCache,\n RouteResolverCacheFactory,\n} from \"@kosmojs/core\";\nimport { pathExists, pathResolver } from \"@kosmojs/lib\";\n\n/**\n * Read the installed package.json at runtime to get the actual version.\n * A static `import ... with { type: \"json\" }` would be inlined by the\n * bundler with the pre-bump version, defeating the point.\n * */\nconst self = JSON.parse(\n readFileSync(\n createRequire(import.meta.url).resolve(\"@kosmojs/dev/package.json\"),\n \"utf-8\",\n ),\n);\n\nexport const cacheFactory: RouteResolverCacheFactory = (\n route,\n sourceFolder,\n extraContext,\n) => {\n const cacheFile = pathResolver(sourceFolder).createPath.libApi(\n dirname(route.file),\n \"cache.json\",\n );\n\n const validateCache = async (cache: RouteResolverCache) => {\n if (!cache?.hash) {\n return;\n }\n\n if (!cache.typeDeclarations || !cache.referencedFiles) {\n // incomplete cache\n return;\n }\n\n const hash = await generateFileHash(route.fileFullpath, {\n ...extraContext,\n });\n\n if (!identicalHashSum(cache.hash, hash)) {\n // route itself updated\n return;\n }\n\n for (const [file, hash] of Object.entries(cache.referencedFiles)) {\n if (\n !identicalHashSum(\n hash,\n await generateFileHash(resolve(sourceFolder.root, file)),\n )\n ) {\n // some referenced file updated\n return;\n }\n }\n\n return cache;\n };\n\n return {\n async get(opt?: { validate?: boolean }) {\n if (await pathExists(cacheFile)) {\n try {\n const cache = JSON.parse(await readFile(cacheFile, \"utf8\"));\n return opt?.validate //\n ? validateCache(cache)\n : cache;\n } catch (_e) {}\n }\n return undefined;\n },\n\n async set(cache) {\n const hash = await generateFileHash(route.fileFullpath, {\n ...extraContext,\n });\n\n const referencedFiles: Record<string, number> = {};\n\n for (const file of cache.referencedFiles) {\n referencedFiles[\n // Strip project root to ensure cached paths are relative\n // and portable across environments (CI, local, etc.)\n file.replace(`${sourceFolder.root}/`, \"\")\n ] = await generateFileHash(file);\n }\n\n const value = { ...cache, hash, referencedFiles };\n\n await mkdir(dirname(cacheFile), { recursive: true });\n await writeFile(cacheFile, JSON.stringify(value, null, 2), \"utf8\");\n\n return value;\n },\n };\n};\n\nconst generateFileHash = async (\n file: string,\n extraContext?: object,\n): Promise<number> => {\n let fileContent: string | undefined;\n try {\n fileContent = await readFile(file, \"utf8\");\n } catch (_e) {\n // file could be deleted since last build\n return 0;\n }\n return fileContent\n ? crc(\n JSON.stringify({\n ...extraContext,\n [self.cacheVersion]: fileContent,\n }),\n )\n : 0;\n};\n\n// return true if sums are identical\nconst identicalHashSum = (a: number, b: number) => {\n return a === b;\n};\n","import http, { type IncomingMessage, type ServerResponse } from \"node:http\";\nimport net from \"node:net\";\nimport { join, resolve } from \"node:path\";\nimport { styleText } from \"node:util\";\n\nimport { build, createServer, type RunnableDevEnvironment } from \"vite\";\n\nimport {\n defaults,\n type GeneratorFactory,\n type GeneratorMeta,\n type GeneratorSignature,\n type ProjectSettings,\n type ResolvedEntry,\n type SourceFolder,\n type WatcherEvent,\n} from \"@kosmojs/core\";\nimport type { DevSetup } from \"@kosmojs/core/api\";\nimport {\n mergeConfigs,\n pathResolver,\n routesFactory,\n spinnerFactory,\n vitePlugins,\n} from \"@kosmojs/lib\";\n\nimport coreGenerator from \"@kosmojs/core-generator\";\n\nimport { cacheFactory } from \"./cache\";\n\nexport default async (\n projectSettings: ProjectSettings,\n): Promise<() => Promise<void>> => {\n const { devPort, command } = projectSettings;\n\n // NOTE: initialize generators before anything else, regardless command\n for (const sourceFolder of projectSettings.sourceFolders) {\n for (const generator of folderGenerators(sourceFolder)) {\n if (!generator.meta?.name || typeof generator.factory !== \"function\") {\n throw new Error(\n `${sourceFolder.name}: Unrecognized generator - must be created via defineGenerator()`,\n );\n }\n\n try {\n await generator.factory(sourceFolder).start?.();\n } catch (error) {\n console.error(\n styleText(\n \"red\",\n `${sourceFolder.name}: ${generator.meta.name} generator failed to initialize`,\n ),\n );\n throw error;\n }\n }\n }\n\n if (command === \"build\") {\n for (const sourceFolder of projectSettings.sourceFolders) {\n const { createPath } = pathResolver(sourceFolder);\n\n const resolvedRoutes = [];\n\n {\n const { resolvers } = await routesFactory(sourceFolder, cacheFactory);\n\n const spinner = spinnerFactory(\n `${sourceFolder.name}: resolving routes`,\n );\n\n for (const { name, handler } of resolvers.values()) {\n spinner.append(\n `[ ${resolvedRoutes.length + 1} of ${resolvers.size} ] ${name}`,\n );\n resolvedRoutes.push(await handler());\n }\n\n spinner.succeed(\"ready ✨\");\n }\n\n const generators = folderGenerators(sourceFolder);\n\n for (const generator of generators) {\n await generator.factory(sourceFolder).build?.(resolvedRoutes);\n }\n\n const plugins = [\n vitePlugins.tsconfigPaths(sourceFolder),\n vitePlugins.nodePrefix(),\n ];\n\n // INFO: === build client ===\n await build(\n mergeConfigs(\n // user-provided config - lowest priority\n sourceFolder.config,\n // generators configs - higher priority\n ...generators.map(({ factory }) => {\n return factory(sourceFolder).config?.({ kind: \"client\", command });\n }),\n // main config - highest priority\n {\n // base provided by sourceFolder.config\n root: createPath.src(),\n cacheDir: cacheDir(sourceFolder, command, \"client\"),\n plugins,\n build: {\n outDir: createPath.distDir(\"client\"),\n manifest: true,\n emptyOutDir: true,\n },\n },\n ),\n );\n\n const apiGenerator = generators.find((e) => e.meta.slot === \"backend\");\n\n // INFO: === build backend ===\n if (apiGenerator) {\n const dir = createPath.distDir(\"api\");\n\n // NOTE: sourceFolder.config is client-specific config - not using for backend!\n // To provide backend-specific config pass it as api generator options.\n await build(\n mergeConfigs(\n // user-provided config - lowest priority\n apiGenerator.options,\n // generator config - higher priority\n apiGenerator\n .factory(sourceFolder)\n .config?.({ kind: \"backend\", command }),\n // main config - highest priority\n {\n base: \"./\",\n root: createPath.src(),\n appType: \"custom\",\n plugins,\n resolve: {\n conditions: [\"node\"],\n },\n build: {\n ssr: true,\n target: \"esnext\",\n sourcemap: true,\n emptyOutDir: true,\n rolldownOptions: {\n input: [\n createPath.api(\"app.ts\"),\n createPath.api(\"server.ts\"),\n ],\n output: {\n dir,\n format: \"esm\",\n },\n },\n },\n cacheDir: cacheDir(sourceFolder, command, \"backend\"),\n },\n ),\n );\n }\n\n for (const generator of generators) {\n await generator.factory(sourceFolder).postBuild?.(resolvedRoutes);\n }\n }\n\n return async () => {};\n }\n\n type RequestMatcher = (req: IncomingMessage) => boolean;\n type RequestHandler = (req: IncomingMessage, res: ServerResponse) => void;\n\n const requestHandlers: Array<\n [\n segments: [base: string, api?: string],\n matcherFactory: () => RequestMatcher,\n handlerFactory: () => RequestHandler,\n ]\n > = [];\n\n const teardownHandlers: Array<() => Promise<unknown>> = [];\n\n const eventMap: Record<string, Awaited<ReturnType<typeof eventFactory>>> = {};\n\n // WARN: call this before starting any server!\n for (const sourceFolder of projectSettings.sourceFolders) {\n eventMap[sourceFolder.name] = await eventFactory(sourceFolder);\n }\n\n let port = await findFreePort(devPort);\n\n for (const sourceFolder of projectSettings.sourceFolders) {\n const { createPath } = pathResolver(sourceFolder);\n\n const requestMatchers = matchersFactory(sourceFolder);\n\n const generators = folderGenerators(sourceFolder);\n\n const plugins = [\n vitePlugins.tsconfigPaths(sourceFolder),\n vitePlugins.nodePrefix(),\n ];\n\n // INFO: === start client server ===\n {\n const viteServer = await createServer(\n mergeConfigs(\n // user-provided config - lowest priority\n sourceFolder.config,\n // generators configs - higher priority\n ...generators.map(({ factory }) => {\n return factory(sourceFolder).config?.({ kind: \"client\", command });\n }),\n // main config - highest priority\n {\n // base provided by sourceFolder.config\n root: createPath.src(),\n cacheDir: cacheDir(sourceFolder, command, \"client\"),\n plugins,\n server: {\n port: port++,\n middlewareMode: true,\n hmr: { port: port++ },\n },\n },\n ),\n );\n\n for (const [evt, handler] of Object.entries(\n eventMap[sourceFolder.name],\n )) {\n viteServer.watcher.on(evt, handler);\n }\n\n requestHandlers.push([\n [sourceFolder.config.base],\n () => requestMatchers.base,\n () => viteServer.middlewares,\n ]);\n\n teardownHandlers.push(viteServer.close);\n }\n\n const apiGenerator = generators.find((e) => e.meta.slot === \"backend\");\n\n // INFO: === start backend server ===\n if (apiGenerator) {\n // NOTE: sourceFolder.config is client-specific config - not using for backend!\n // To provide backend-specific config pass it as api generator options.\n const viteServer = await createServer(\n mergeConfigs(\n // user-provided config - lowest priority\n apiGenerator.options,\n // generator config - higher priority\n apiGenerator\n .factory(sourceFolder)\n .config?.({ kind: \"backend\", command }),\n // main config - highest priority\n {\n root: createPath.src(),\n appType: \"custom\",\n cacheDir: cacheDir(sourceFolder, command, \"backend\"),\n plugins,\n server: {\n port: port++,\n middlewareMode: true,\n hmr: { port: port++ },\n },\n resolve: {\n conditions: [\"node\"],\n },\n environments: {\n api: {\n resolve: {\n conditions: [\"node\"],\n },\n },\n },\n },\n ),\n );\n\n const env = viteServer.environments.api as RunnableDevEnvironment;\n\n const loadDevSetup = async () => {\n env.runner.clearCache();\n return env.runner\n .import<{ default: DevSetup }>(join(defaults.apiDir, \"dev.ts\"))\n .then((e) => e.default);\n };\n\n let devSetup = await loadDevSetup();\n\n for (const [evt, handler] of Object.entries(\n eventMap[sourceFolder.name],\n )) {\n viteServer.watcher.on(evt, async (file) => {\n const mods = env.moduleGraph.getModulesByFile(file);\n if (mods?.size) {\n try {\n await handler(file);\n await devSetup?.teardownHandler?.();\n devSetup = await loadDevSetup();\n } catch (error) {\n /**\n * A transiently-invalid module (half-saved file, atomic rewrite mid-flight, syntax error)\n * must not take the dev server down: an uncaught rejection here is fatal to the whole process.\n * Keep serving the previous api program; the next successful change reloads it.\n * */\n console.error(\n styleText(\n \"red\",\n `${sourceFolder.name}: api reload failed - keeping previous api program`,\n ),\n );\n console.error(error);\n }\n }\n });\n }\n\n requestHandlers.push([\n [sourceFolder.config.base, sourceFolder.config.apiBase],\n () => devSetup.requestMatcher || requestMatchers.api,\n () => devSetup.requestHandler(),\n ]);\n\n teardownHandlers.push(viteServer.close);\n }\n }\n\n /**\n * Sorting is essential to ensure more specific paths are matched before broader ones.\n *\n * Given source folders:\n * - main app: baseurl=/ apiurl=/api\n * - admin app: baseurl=/admin apiurl=/admin/api\n *\n * Correct sort order:\n * 1. /admin/api — most specific\n * 2. /api\n * 3. /admin\n * 4. /\n * */\n const requestHandlerWeight = ([\n segments,\n ]: (typeof requestHandlers)[number]) => {\n const [base, api] = segments;\n /**\n * set weight to number of non-empty segments:\n * \"/\" weight is 0\n * \"/admin\" weight is 1\n * */\n const weight = base.split(\"/\").filter(Boolean).length;\n return api ? weight + 5 : weight;\n };\n\n const handlers = requestHandlers.sort(\n (a, b) => requestHandlerWeight(b) - requestHandlerWeight(a),\n );\n\n const httpServer = http.createServer((req, res) => {\n for (const [, matcherFactory, handlerFactory] of handlers) {\n // should be called on every request\n const [matcher, handler] = [matcherFactory(), handlerFactory()];\n if (matcher(req)) {\n handler(req, res);\n return;\n }\n }\n res.writeHead(404, { \"Content-Type\": \"text/html\" });\n res.end(\"<h1>404: Not Found</h1>\");\n });\n\n httpServer.on(\"error\", (error) => {\n console.error(\n styleText(\"red\", `Failed to start dev server on port ${devPort}`),\n );\n console.error(error.message);\n process.exit(1);\n });\n\n httpServer.listen(devPort);\n\n teardownHandlers.push(async () => {\n httpServer.close();\n });\n\n return async () => {\n for (const handler of teardownHandlers) {\n await handler();\n }\n // Let chokidar fully release file handles\n await new Promise((resolve) => setTimeout(resolve, 100));\n };\n};\n\nconst cacheDir = (\n { root, name }: SourceFolder,\n command: ProjectSettings[\"command\"],\n mode: \"client\" | \"backend\",\n) => {\n return resolve(root, `var/.vite/${name}/${command}/${mode}`);\n};\n\nconst folderGenerators = (\n sourceFolder: SourceFolder,\n): Array<GeneratorSignature> => {\n const { generators = [] } = sourceFolder.config;\n\n const baseGenerators: Partial<\n Record<NonNullable<GeneratorMeta[\"slot\"]>, GeneratorSignature>\n > = {};\n\n const userGenerators: Array<GeneratorSignature> = [];\n\n for (const generator of generators) {\n if (generator.meta.slot) {\n baseGenerators[generator.meta.slot] = generator;\n } else {\n userGenerators.push(generator);\n }\n }\n\n return [\n // core generator should run first\n coreGenerator(),\n // then backend generator\n ...(baseGenerators.backend ? [baseGenerators.backend] : []),\n // then fetch generator, only if backend generator also enabled\n ...(baseGenerators.fetch && baseGenerators.backend\n ? [baseGenerators.fetch]\n : []),\n // then user generators in the order they were added\n ...userGenerators,\n // ssr generator should run after user generators\n ...(baseGenerators.ssr ? [baseGenerators.ssr] : []),\n // ssg generator should run after ssr generator\n ...(baseGenerators.ssg ? [baseGenerators.ssg] : []),\n ];\n};\n\nconst eventFactory = async (\n sourceFolder: SourceFolder,\n): Promise<\n Record<\"add\" | \"change\" | \"unlink\", (f: string) => Promise<void>>\n> => {\n const { resolvers, resolversFactory } = await routesFactory(\n sourceFolder,\n cacheFactory,\n );\n\n const { createPath } = pathResolver(sourceFolder);\n\n const generators: Array<{\n name: string | undefined;\n factory: GeneratorFactory;\n }> = [];\n\n for (const generator of folderGenerators(sourceFolder)) {\n const factory = generator.factory(sourceFolder);\n generators.push({ name: generator.meta.name, factory });\n }\n\n const resolvedRoutes = new Map<\n string, // fileFullpath\n ResolvedEntry\n >();\n\n const runGenerators = async (event?: WatcherEvent) => {\n /**\n * Watch handlers receive the full list of entries\n * and should process only those whose source file or dependencies were updated.\n * */\n const entries = Array.from(resolvedRoutes.values());\n\n for (const { name, factory } of generators) {\n try {\n await factory.watch?.(entries, event);\n } catch (error) {\n console.error(\n styleText(\"red\", `${sourceFolder.name}: ${name} generator failed`),\n );\n if (event) {\n console.error(event);\n }\n console.error(error);\n }\n }\n };\n\n const updateResolvedEntry = async (file: string) => {\n const resolver = resolvers.get(file);\n\n if (!resolver) {\n return;\n }\n\n try {\n const resolvedEntry = await resolver.handler(file);\n resolvedRoutes.set(resolvedEntry.entry.fileFullpath, resolvedEntry);\n return resolvedEntry;\n } catch (error) {\n const route = file.replace(`${createPath.api()}/`, \"\");\n console.error(\n styleText(\n \"red\",\n `${sourceFolder.name}: ${route} route resolution failed`,\n ),\n );\n console.error(error);\n return;\n }\n };\n\n {\n const spinner = spinnerFactory(`${sourceFolder.name}: resolving routes`);\n for (const { name, handler } of resolvers.values()) {\n spinner.append(\n `[ ${resolvedRoutes.size + 1} of ${resolvers.size} ] ${name}`,\n );\n const route = await handler();\n resolvedRoutes.set(route.entry.fileFullpath, route);\n }\n spinner.succeed(\"ready ✨\");\n }\n\n // NOTE: call only after routes resolved\n await runGenerators();\n\n return {\n async add(file: string) {\n const [resolver] = resolversFactory([file]).values();\n\n if (!resolver) {\n return;\n }\n\n resolvers.set(file, resolver);\n\n // call only after `resolvers.set(file)`\n const resolvedEntry = await updateResolvedEntry(file);\n\n if (resolvedEntry) {\n await runGenerators({ kind: \"create\", file });\n }\n },\n\n async change(file: string) {\n if (resolvedRoutes.has(file)) {\n // route updated\n await updateResolvedEntry(file);\n } else {\n // updating entries that are referencing updated file\n const relatedRoutes = resolvedRoutes\n .values()\n .flatMap(({ kind, entry }) => {\n return kind === \"apiRoute\"\n ? entry.referencedFiles.includes(file)\n ? [entry]\n : []\n : [];\n });\n\n for (const route of relatedRoutes) {\n await updateResolvedEntry(route.fileFullpath);\n }\n }\n\n await runGenerators({ kind: \"update\", file });\n },\n\n async unlink(file) {\n // route deleted\n resolvers.delete(file);\n resolvedRoutes.delete(file);\n await runGenerators({ kind: \"delete\", file });\n },\n };\n};\n\nconst normalizeRegex = (s: string) => {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\/+$/, \"\");\n};\n\nconst matchersFactory: (\n sourceFolder: SourceFolder,\n) => Record<\"base\" | \"api\", (req: IncomingMessage) => boolean> = ({\n config,\n}) => {\n const basePattern = new RegExp(`^${normalizeRegex(config.base)}(?=$|/)`);\n const apiPattern = new RegExp(\n `^${normalizeRegex(join(config.base, config.apiBase))}(?=$|/)`,\n );\n return {\n base(req) {\n return apiPattern.test(req.url as string)\n ? false\n : basePattern.test(req.url as string);\n },\n api(req) {\n return apiPattern.test(req.url as string);\n },\n };\n};\n\nconst findFreePort = async (devPort: number): Promise<number> => {\n let minPort = 0;\n let maxPort = 0;\n\n for (const n of [3, 2, 1, \"\"]) {\n minPort = Number(`${n}${devPort}`) + 100;\n maxPort = minPort + 100;\n if (maxPort < 65000) {\n break;\n }\n }\n\n if (maxPort >= 65000) {\n throw new Error(\"the devPort in package.json should be less than 64000\");\n }\n\n const range = maxPort - minPort + 1;\n const startOffset = Math.floor(Math.random() * range);\n\n const ports = Array.from({ length: range }, (_, i) => {\n return minPort + ((startOffset + i) % range);\n });\n\n const freePort = await ports.reduce(\n async (prevPromise, port) => {\n const freePort = await prevPromise;\n if (freePort) {\n return freePort;\n }\n const isFree = await isPortFree(port);\n return isFree ? port : undefined;\n },\n Promise.resolve(undefined as number | undefined),\n );\n\n if (!freePort) {\n throw new Error(`No free ports found in range ${minPort}-${maxPort}`);\n }\n\n return freePort;\n};\n\nconst isPortFree = (port: number): Promise<boolean> => {\n return new Promise((resolve) => {\n const server = net.createServer();\n\n server.once(\"error\", () => resolve(false));\n\n server.once(\"listening\", () => {\n server.close();\n resolve(true);\n });\n\n server.listen(port, \"127.0.0.1\");\n });\n};\n"],"mappings":"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"}
|