@kosmojs/dev 0.5.0 → 0.5.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/pkg/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import "node:module";
2
2
  import { styleText } from "node:util";
3
- import { containsPathTraversalPatterns, createAliasPatterns, createH3Pattern, createHonoPattern, createPathPattern, createWatchedApiRouteEntriesFilter, createWatchedPageRouteEntriesFilter, defineGenerator, defineGeneratorFactory, mergeConfigs, nestedRoutesFactory, pathExists, pathResolver, pathTokensFactory, renderFactory, renderToFile, sortRoutes, sortRoutesForResolution, spinnerFactory, vitePlugins } from "@kosmojs/lib";
3
+ import { containsPathTraversalPatterns, createAliasPatterns, createH3Pattern, createHonoPattern, createPathPattern, createWatchedApiRouteEntriesFilter, createWatchedPageRouteEntriesFilter, defineGenerator, defineGeneratorFactory, generateTsconfig, mergeConfigs, nestedRoutesFactory, pathExists, pathResolver, pathTokensFactory, renderFactory, renderToFile, sortRoutes, sortRoutesForResolution, spinnerFactory, vitePlugins } from "@kosmojs/lib";
4
4
  import { dirname, join, posix, relative, resolve } from "node:path";
5
- import semver from "semver";
6
5
  import { RequestBodyTargets, RequestValidationTargets, TYPEBOX_MESSAGE_CODES, createRouteResolver, createTemplateResolver, defaults } from "@kosmojs/core";
7
6
  import { routeRenderHelpers } from "@kosmojs/core/generators";
8
7
  import { HTTPMethods } from "@kosmojs/core/fetch";
@@ -45,7 +44,8 @@ var package_default$11 = {
45
44
  type: "module",
46
45
  "private": true,
47
46
  name: "@kosmojs/core-generator",
48
- version: "0.3.0",
47
+ version: "0.0.0",
48
+ description: "Baseline file generation shared by all KosmoJS generators (internal)",
49
49
  author: "Slee Woo",
50
50
  license: "MIT",
51
51
  files: ["pkg/*"],
@@ -56,142 +56,34 @@ var package_default$11 = {
56
56
  scripts: { "build": "wsbuild src/index.ts" },
57
57
  dependencies: {
58
58
  "@kosmojs/core": "workspace:^",
59
- "@kosmojs/lib": "workspace:^",
60
- "semver": "^7.8.5"
59
+ "@kosmojs/lib": "workspace:^"
61
60
  },
62
- devDependencies: {
63
- "@types/semver": "^7.8.0",
64
- "path-to-regexp": "^8.4.2"
65
- }
61
+ devDependencies: { "path-to-regexp": "^8.4.2" }
66
62
  };
67
63
  var config_default = "export const base = {{base}};\nexport const backendBase = {{backendBase}};\nexport const backendAliasPatterns = {{backendAliasPatterns}};\n";
68
64
  var core_default = "import type { StaticParams } from \"./types\";\n\nexport * from \"./config\";\nexport * from \"./routes\";\nexport * from \"./types\";\n\nexport const defineStaticParams = <T extends keyof StaticParams>(\n variants: Array<StaticParams[T]>,\n) => variants;\n";
69
- var routeMapper_default = "{{> routeMapperPartial}}\n\nimport { base, backendBase } from \"./config\";\n\n// used by backend generators and fetch clients\nexport const apiRouteMap = {\n {{#each apiRoutes}}\n \"{{name}}\": apiRouteMapper<[{{serializeParamsTupleElements .}}]>(\n backendBase,\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";
70
- var routeMapperPartial_default = "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 { createHost, join } from \"@kosmojs/core/fetch\";\nimport type { RoutePathMethods } from \"@kosmojs/core/generators\";\n\ntype NormalizeParams = (path: string) => Record<string, unknown>;\n\ntype NormalizeSearchParams = (\n searchParams: Record<string, unknown>,\n method: string,\n) => Record<string, unknown>;\n\ntype PayloadResolver = <T>(\n payload: Record<ValidationTarget, T> | undefined,\n target: ValidationTarget,\n method: string,\n) => T | Record<string, unknown> | undefined;\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: NormalizeParams;\n normalizeSearchParams: NormalizeSearchParams;\n payloadResolver: PayloadResolver;\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: NormalizeSearchParams = (\n searchParams,\n method,\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: NormalizeParams = (path) => {\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: PayloadResolver = (payload, target, method) => {\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 }, {});\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";
65
+ var routeMapper_default = "{{> routeMapperPartial}}\n\nimport { base, backendBase } from \"./config\";\n\nexport const apiRouteMap = {\n {{#each apiRoutes}}\n \"{{name}}\": apiRouteMapper<[{{serializeParamsTupleElements .}}]>(\n backendBase,\n {{serializeApiRoute .}},\n ),\n {{/each}}\n};\n\nexport const pageRouteMap = {\n {{#each pageRoutes}}\n \"{{name}}\": pageRouteMapper<[{{serializeParamsTupleElements .}}]>(\n base,\n {{serializePageRoute .}},\n ),\n {{/each}}\n};\n";
66
+ var routeMapperPartial_default = "import { compile } from \"path-to-regexp\";\n\nimport {\n type ApiRouteSerialized,\n maybeNumber,\n type PageRouteSerialized,\n stringifySearchParams,\n} from \"@kosmojs/core\";\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 route: ApiRouteSerialized,\n): ApiRouteSerialized & RoutePathMethods<ParamsT> => {\n const { name, pathPattern, params, numericProperties } = route;\n\n const toPath = compile(join(base, pathPattern));\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 return { ...route, paramsMapper, parametrize, path, href };\n};\n\nexport const pageRouteMapper = <ParamsT extends readonly unknown[]>(\n base: string,\n route: PageRouteSerialized,\n): PageRouteSerialized & RoutePathMethods<ParamsT> => {\n const toPath = compile(join(base, 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 { ...route, paramsMapper, parametrize, path, href };\n};\n";
71
67
  var ssr_default$2 = "import { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport type RequestContext = {\n headers?: HeadersInit;\n tsqClient?: unknown;\n error?: unknown;\n};\n\n/**\n * Request-scoped context store.\n * */\nexport const store = new AsyncLocalStorage<RequestContext>();\n\n/**\n * Origin used to absolutize the relative URLs the client produces.\n * */\nexport const ssrOrigin = \"http://ssr.local\";\n\nexport const redirectCodes = [\n // Moved Permanently\n 301,\n // Found (temporary)\n 302,\n // See Other (redirect after POST)\n 303,\n // Temporary Redirect (preserves method)\n 307,\n // Permanent Redirect (preserves method)\n 308,\n];\n";
72
68
  var types_default = "export type Override<A, B> = Omit<A, keyof B> & B;\n\nexport type StaticParams = {\n {{#each pageRoutes}}\n \"{{name}}\": [ {{serializeParamsTupleElements .}} ];\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";
73
69
  var env_d_default$8 = "declare module \"virtual:kosmo/env\" {\n export const command: \"serve\" | \"build\";\n}\n\ndeclare module \"virtual:kosmo/backend-app\" {\n import type { FetchApp, NodeApp } from \"@kosmojs/core\";\n const backend: FetchApp | NodeApp | undefined;\n export default backend;\n}\n\ndeclare module \"virtual:kosmo/fetch-transport\" {\n import type { Transport } from \"@kosmojs/core/fetch\";\n /**\n * Undefined on the client, where fetch clients fall back to global fetch.\n * On the SSR bundle it dispatches straight into the backend app, in process.\n * Supplied by the `kosmo:virtualModules` Vite plugin - there is no file.\n * */\n export const transport: Transport | undefined;\n}\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";
74
70
  var gitignore_default = "# 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";
75
71
  var global_d_default = "export declare global {\n interface Window {\n __KOSMO_HYDRATION_BOOL__: boolean;\n __KOSMO_HYDRATION_DATA__: Record<string, unknown> | undefined;\n }\n}\n";
76
- var templates_default = "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n</head>\n<body>\n <div id=\"app\"><!--app-html--></div>\n <script type=\"module\" src=\"/{{ entryDir }}/client.ts\"><\/script>\n</body>\n</html>\n";
77
72
  var schemas_default$1 = "// stub schemas, specialized generators supposed to overwrite this file\nimport type { ValidationSchemas } from \"@kosmojs/core\";\nexport type { ValidationSchemas };\nexport const validationSchemas: ValidationSchemas = {};\n";
78
- var generateTsconfig = ({ dependencies, devDependencies }, sourceFolder) => {
79
- const rootDir = "${configDir}";
80
- const deps = Object.keys({
81
- ...dependencies,
82
- ...devDependencies
83
- });
84
- const compilerOptions = {
85
- types: ["vite/client", ...[
86
- "node",
87
- "deno",
88
- "bun"
89
- ].flatMap((e) => {
90
- return deps.includes(`@types/${e}`) ? [`@types/${e}`] : [];
91
- })],
92
- moduleResolution: "bundler",
93
- module: "ESNext",
94
- target: "ESNext",
95
- strict: true,
96
- exactOptionalPropertyTypes: true,
97
- noImplicitAny: true,
98
- noImplicitThis: true,
99
- noImplicitOverride: true,
100
- noImplicitReturns: true,
101
- noUnusedLocals: false,
102
- noUnusedParameters: false,
103
- allowArbitraryExtensions: true,
104
- allowImportingTsExtensions: true,
105
- allowUnreachableCode: false,
106
- allowUnusedLabels: false,
107
- useUnknownInCatchVariables: true,
108
- noFallthroughCasesInSwitch: true,
109
- noUncheckedSideEffectImports: true,
110
- resolveJsonModule: true,
111
- esModuleInterop: true,
112
- verbatimModuleSyntax: true,
113
- skipLibCheck: true,
114
- noEmit: true
115
- };
116
- if (sourceFolder) return {
117
- include: [
118
- `${rootDir}/`,
119
- `${rootDir}/../../${defaults.libDir}/${sourceFolder}/`,
120
- `${rootDir}/../../**/*.d.ts`
121
- ],
122
- compilerOptions: {
123
- ...compilerOptions,
124
- types: [...compilerOptions.types],
125
- paths: {
126
- [`${defaults.appPrefix}/*`]: [`${rootDir}/../../*`],
127
- [`${defaults.srcPrefix}/*`]: [`${rootDir}/*`],
128
- [`${defaults.libPrefix}/*`]: [`${rootDir}/../../${defaults.libDir}/${sourceFolder}/*`]
129
- }
130
- }
131
- };
132
- return {
133
- include: [`${rootDir}/`],
134
- exclude: [`${rootDir}/${defaults.srcDir}/`],
135
- compilerOptions: {
136
- ...compilerOptions,
137
- paths: { [`${defaults.appPrefix}/*`]: [`${rootDir}/*`] }
138
- }
139
- };
140
- };
141
- /**
142
- * Generates stub files required by various generators.
143
- * Ensures cross-generator dependencies remain resolvable
144
- * even if specialized generators supposed to generate these files are not installed.
145
- * */
73
+ var sidecar_default = "import type { SidecarService } from \"@kosmojs/core\";\n\nexport const defineService = (s: SidecarService) => s;\n";
74
+ var src_default$1$1 = "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/favicon.svg\" />\n</head>\n<body>\n <div id=\"app\"><!--app-html--></div>\n <script type=\"module\" src=\"/{{ entryDir }}/client.ts\"><\/script>\n</body>\n</html>\n";
75
+ var entry_default = "import { defineService } from \"{{ createImport 'lib' 'sidecar' }}\";\n\nexport default defineService({\n async start() {\n /* start the service;\n * relevant only if sidecar.serve is true in kosmo.config.ts\n * */\n return async () => {\n /* close the server;\n * highly recommended in dev mode, to avoid EADDRINUSE errors\n * */\n }\n },\n async teardown() {\n /* close outbound connections, if any (database, socket, etc.);\n * called before closing the server\n * */\n },\n});\n";
76
+ var run_default = "import service from \"./entry\"\n\nconst close = await service.start();\n\nfor (const signal of [\"SIGINT\", \"SIGTERM\"]) {\n process.on(signal, async () => {\n await service.teardown?.();\n await close();\n process.exit(0);\n });\n}\n";
146
77
  var factory_default$13 = defineGeneratorFactory((sourceFolder) => {
147
78
  const { createPath, createImportHelpers } = pathResolver(sourceFolder);
148
- const { generators, frontend, backend } = sourceFolder.config;
149
- const start = async () => {
79
+ const { frontend, backend } = sourceFolder.config;
80
+ const seed = async () => {
150
81
  const { dependencies = {}, devDependencies = {} } = await import(resolve(sourceFolder.root, "package.json"), { with: { type: "json" } }).then((m) => m.default);
151
82
  {
152
- const missing = [];
153
- const outdated = [];
154
- const required = generators.flatMap((generator) => {
155
- return ["dependencies", "devDependencies"].flatMap((key) => {
156
- return generator[key] ? Object.entries(typeof generator[key] === "function" ? generator[key](generator.options) : generator[key]).flatMap(([name, v]) => {
157
- const minVersion = semver.minVersion(v)?.version;
158
- return minVersion ? [[
159
- name,
160
- minVersion,
161
- key
162
- ]] : [];
163
- }) : [];
164
- });
165
- });
166
- for (const [name, minVersion, key] of required) {
167
- const rawVersion = dependencies[name] || devDependencies[name];
168
- const version = rawVersion ? semver.minVersion(rawVersion)?.version : void 0;
169
- if (!rawVersion || !version) missing.push([
170
- name,
171
- minVersion,
172
- key
173
- ]);
174
- else if (semver.lt(version, minVersion)) outdated.push([
175
- name,
176
- minVersion,
177
- key
178
- ]);
179
- }
180
- if (missing.length) {
181
- console.error(styleText(["red", "italic"], `There are ${missing.length} missing dependencies, please consider installing them.`));
182
- for (const key of ["dependencies", "devDependencies"]) {
183
- const deps = missing.filter((e) => e[2] === key);
184
- if (deps.length) console.error(`${key}: ${styleText(["blue"], deps.map(([name]) => name).join(" "))}`);
185
- }
186
- }
187
- if (outdated.length) {
188
- console.error(styleText(["yellow", "italic"], `There are ${outdated.length} outdated dependencies, please consider updating them:`));
189
- console.error(outdated.map(([name]) => name).join(" "));
190
- console.error();
191
- }
192
- }
193
- {
194
- await renderToFile(resolve(sourceFolder.root, "tsconfig.json"), JSON.stringify({ extends: `./${defaults.libDir}/tsconfig.json` }, void 0, 2), {}, { overwrite: false });
83
+ await renderToFile(resolve(sourceFolder.root, "tsconfig.json"), JSON.stringify({
84
+ extends: `./${defaults.libDir}/tsconfig.json`,
85
+ include: [`./${defaults.libDir}/*.d.ts`]
86
+ }, void 0, 2), {}, { overwrite: false });
195
87
  await renderToFile(createPath.lib("../tsconfig.json"), JSON.stringify(generateTsconfig({
196
88
  dependencies,
197
89
  devDependencies
@@ -203,7 +95,7 @@ var factory_default$13 = defineGeneratorFactory((sourceFolder) => {
203
95
  }, sourceFolder.name);
204
96
  const compilerOptions = {};
205
97
  const types = new Set(tsconfig.compilerOptions.types || []);
206
- for (const { meta } of generators) {
98
+ for (const { meta } of sourceFolder.generators) {
207
99
  if (meta.jsx) compilerOptions.jsx = meta.jsx;
208
100
  if (meta.jsxImportSource) compilerOptions.jsxImportSource = meta.jsxImportSource;
209
101
  for (const type of meta.types || []) types.add(type);
@@ -223,7 +115,11 @@ var factory_default$13 = defineGeneratorFactory((sourceFolder) => {
223
115
  * except cache.json files; if file exists, do not override.
224
116
  * */
225
117
  await renderToFile(createPath.lib("../.gitignore"), gitignore_default, {}, { overwrite: false });
226
- if (generators.some((e) => e.meta.slot === "frontend")) await renderToFile(createPath.src("index.html"), templates_default, { entryDir: defaults.entryDir }, { overwrite: (c) => !c?.trim() });
118
+ if (sourceFolder.config.frontend) await renderToFile(createPath.src("index.html"), src_default$1$1, { entryDir: defaults.entryDir }, { overwrite: (c) => !c?.trim() });
119
+ if (sourceFolder.config.sidecar) {
120
+ await renderToFile(createPath.lib("sidecar.ts"), sidecar_default, {});
121
+ for (const [file, template] of [["entry.ts", entry_default], ["run.ts", run_default]]) await renderFactory({ helpers: createImportHelpers({ origin: "src" }) }).renderToFile(createPath.src(file), template, {}, { overwrite: false });
122
+ }
227
123
  };
228
124
  const generateLibFiles = async (entries) => {
229
125
  const { renderToFile } = renderFactory({
@@ -258,7 +154,7 @@ var factory_default$13 = defineGeneratorFactory((sourceFolder) => {
258
154
  for (const { kind, entry } of entries) if (kind === "apiRoute") await renderToFile(createPath.libApi(dirname(entry.file), "schemas.ts"), schemas_default$1, { route: entry }, { overwrite: false });
259
155
  };
260
156
  return {
261
- start,
157
+ seed,
262
158
  watch: generateLibFiles,
263
159
  build: generateLibFiles,
264
160
  virtualModules() {
@@ -266,7 +162,7 @@ var factory_default$13 = defineGeneratorFactory((sourceFolder) => {
266
162
  return [{
267
163
  specifier: "virtual:kosmo/backend-app",
268
164
  csr: "export default undefined;",
269
- ssr: generators.some((e) => e.meta.slot === "backend") ? `export { default } from "${createImport.api(["app"], { origin: "lib" })}";` : "export default undefined;"
165
+ ssr: sourceFolder.config.backend ? `export { default } from "${createImport.api(["app"], { origin: "lib" })}";` : "export default undefined;"
270
166
  }];
271
167
  }
272
168
  };
@@ -287,7 +183,8 @@ var package_default$10 = {
287
183
  type: "module",
288
184
  "private": true,
289
185
  name: "@kosmojs/fetch-generator",
290
- version: "0.3.0",
186
+ version: "0.0.0",
187
+ description: "Typed isomorphic fetch clients for KosmoJS backends (internal)",
291
188
  author: "Slee Woo",
292
189
  license: "MIT",
293
190
  files: ["pkg/*"],
@@ -306,7 +203,7 @@ var package_default$10 = {
306
203
  devDependencies: { "light-my-request": "^6.6.0" }
307
204
  };
308
205
  var fetch_default = "{{#each routes}}\nimport {{id}} from \"{{ createImport 'libApi' name 'fetch' }}\";\n{{/each}}\n\nexport { ValidationError } from \"@kosmojs/core/errors\";\n\nexport type ResponseT = {\n{{#each routes}}\n \"{{name}}\": import(\"{{ createImport 'libApi' name 'fetch' }}\").ResponseT;\n{{/each}}\n}\n\nexport default {\n{{#each routes}}\n \"{{name}}\": {{id}},\n{{/each}}\n}\n";
309
- var route_default$3 = "import fetchFactory from \"@kosmojs/core/fetch\";\n\nimport { backendBase, apiRouteMap } from \"{{ createImport 'libCore' }}\";\nimport { transport } from \"virtual:kosmo/fetch-transport\";\n\nimport {\n type MaybeWrapped,\n unwrap,\n} from \"{{ createImport 'lib' 'unwrap' }}\";\n\n{{#each validationTypes}}\nimport type { {{id}} } from \"./types\";\n{{/each}}\n\nimport { validationSchemas } from \"./schemas\";\n\nexport { ValidationError } from \"@kosmojs/core/errors\";\n\nexport { validationSchemas };\n\nexport type ParamsT = [\n {{serializeParamsTupleElements route}}\n];\n\n{{#each payloadTypes}}\nexport type {{name}} = {\n {{#each types}}{{method}}: {{id}};\n {{/each}}\n}\n{{/each}}\n\nexport type ResponseT = {\n {{#each responseTypes}}\n {{method}}: {{#each types}}{{#unless @first}}|{{/unless}} {{id}}{{/each}};\n {{/each}}\n}\n\nconst {\n paramsMapper,\n parametrize,\n payloadResolver,\n path,\n href,\n} = apiRouteMap[\"{{route.name}}\"];\n\nconst fetchApi = fetchFactory(backendBase, { transport });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n _payload?: {\n {{#each payloadTypes}}\n {{target}}: MaybeWrapped<{{id}}>,\n {{/each}}\n },\n opt?: {\n unwrap?: <T>(data: MaybeWrapped<T>) => T;\n },\n): Promise<\n {{#if responseType}}\n ResponseT[\"{{method}}\"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [_params, _payload].map((data) => {\n return typeof opt?.unwrap === \"function\" ? opt.unwrap(data) : unwrap(data);\n })\n // validate only on client, skip double validation in SSR mode\n if (!import.meta.env.SSR) {\n if (validationSchemas.params) {\n validationSchemas.params.validate(\n paramsMapper(params as never, { coerceNumbers: true }),\n );\n }\n {{#each payloadTypes}}\n if (validationSchemas.{{target}}?.{{method}}) {\n validationSchemas.{{target}}.{{method}}.validate(\n payloadResolver(payload as never, \"{{target}}\", \"{{../method}}\"),\n );\n }\n {{/each}}\n }\n return fetchApi.{{method}}(parametrize(params as never), payload as never);\n};\n{{/each}}\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n";
206
+ var route_default$3 = "import fetchFactory, { payloadResolver } from \"@kosmojs/core/fetch\";\n\nimport { apiRouteMap } from \"{{ createImport 'libCore' }}\";\nimport { transport } from \"virtual:kosmo/fetch-transport\";\n\nimport {\n type MaybeWrapped,\n unwrap,\n} from \"{{ createImport 'lib' 'unwrap' }}\";\n\n{{#each validationTypes}}\nimport type { {{id}} } from \"./types\";\n{{/each}}\n\nimport { validationSchemas } from \"./schemas\";\n\nexport { ValidationError } from \"@kosmojs/core/errors\";\n\nexport { validationSchemas };\n\nexport type ParamsT = [\n {{serializeParamsTupleElements route}}\n];\n\n{{#each payloadTypes}}\nexport type {{name}} = {\n {{#each types}}{{method}}: {{id}};\n {{/each}}\n}\n{{/each}}\n\nexport type ResponseT = {\n {{#each responseTypes}}\n {{method}}: {{#each types}}{{#unless @first}}|{{/unless}} {{id}}{{/each}};\n {{/each}}\n}\n\nconst {\n paramsMapper,\n parametrize,\n path,\n href,\n} = apiRouteMap[\"{{route.name}}\"];\n\nconst fetchApi = fetchFactory({ transport });\n\n{{#each routeMethods}}\nexport const {{method}} = (\n _params{{#if ../route.optionalParams}}?{{/if}}: MaybeWrapped<ParamsT>,\n _payload?: {\n {{#each payloadTypes}}\n {{target}}: MaybeWrapped<{{id}}>,\n {{/each}}\n },\n opt?: {\n unwrap?: <T>(data: MaybeWrapped<T>) => T;\n },\n): Promise<\n {{#if responseType}}\n ResponseT[\"{{method}}\"]\n {{else}}\n unknown\n {{/if}}\n> => {\n const [params, payload] = [_params, _payload].map((data) => {\n return typeof opt?.unwrap === \"function\" ? opt.unwrap(data) : unwrap(data);\n })\n // validate only on client, skip double validation in SSR mode\n if (!import.meta.env.SSR) {\n if (validationSchemas.params) {\n validationSchemas.params.validate(\n paramsMapper(params as never, { coerceNumbers: true }),\n );\n }\n {{#each payloadTypes}}\n if (validationSchemas.{{target}}?.{{method}}) {\n validationSchemas.{{target}}.{{method}}.validate(\n payloadResolver(\n payload as never,\n \"{{target}}\",\n \"{{../method}}\",\n apiRouteMap[\"{{@root/route.name}}\"],\n ),\n );\n }\n {{/each}}\n }\n return fetchApi.{{method}}(parametrize(params as never), payload as never);\n};\n{{/each}}\n\nexport default {\n {{#each routeMethods}}\n {{method}},\n {{/each}}\n path,\n href,\n validationSchemas,\n};\n";
310
207
  var ssr_default$1 = "import backend from \"virtual:kosmo/backend-app\";\n\nimport {\n redirectCodes,\n ssrOrigin,\n store,\n} from \"{{ createImport 'libCore' 'ssr' }}\";\n\n/**\n * Maximum redirect hops, mirroring the fetch spec limit.\n * */\nexport const maxRedirects = 5;\n\n/**\n * HeadersProvider for createTransport.\n * */\nconst headersProvider = () => {\n return store.getStore()?.headers;\n};\n\nconst createDispatch = (app) => {\n return typeof app.fetch === \"function\"\n ? app.fetch\n : async (request) => {\n const { inject } = await import(\"light-my-request\");\n\n /**\n * Node dispatch: serializes the web Request into light-my-request's\n * injection format and lifts the injected response back into a web Response.\n * */\n const url = new URL(request.url);\n\n const payload = [\"GET\", \"HEAD\"].includes(request.method)\n ? undefined\n : Buffer.from(await request.arrayBuffer());\n\n const result = await inject(app.callback(), {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n ...(payload?.length ? { payload } : {}),\n });\n\n const headers = new Headers();\n\n for (const [key, value] of Object.entries(result.headers)) {\n for (const entry of Array.isArray(value) ? value : [value]) {\n if (entry !== undefined) {\n headers.append(key, String(entry));\n }\n }\n }\n\n /**\n * 204/304 responses must not carry a body per the Response\n * constructor contract.\n * */\n const body = [204, 304].includes(result.statusCode)\n ? null\n : new Uint8Array(result.rawPayload);\n\n return new Response(body, {\n status: result.statusCode,\n statusText: result.statusMessage,\n headers,\n });\n };\n};\n\nconst createTransport = (app) => {\n const dispatch = createDispatch(app);\n\n /**\n * Build a fetch-compatible transport that dispatches requests\n * directly into the given app - no sockets, no interception.\n * Redirects are followed in-process, including the 303 and 301/302 method rewrite to GET.\n * */\n return async (input, init) => {\n /**\n * Request-scoped headers act as defaults: anything set explicitly\n * on the call itself wins over forwarded values.\n * */\n const headers = new Headers(init?.headers);\n\n // When the body is FormData, the Request constructor sets a multipart\n // Content-Type with a fresh boundary. A forwarded Content-Type default would\n // override that boundary and desync it from the serialized body, so never\n // forward Content-Type for FormData bodies.\n const isFormBody = init?.body instanceof FormData;\n\n for (const [key, value] of new Headers(headersProvider() || undefined)) {\n if (isFormBody && key.toLowerCase() === \"content-type\") {\n continue;\n }\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n\n let request = new Request(new URL(String(input), ssrOrigin), {\n ...init,\n headers,\n });\n\n /**\n * Bodies are buffered once so they can be replayed across\n * 307/308 hops; the client only ever sends strings, FormData\n * and buffer-ish payloads, so this is safe and cheap.\n * */\n const body = [\"GET\", \"HEAD\"].includes(request.method)\n ? undefined\n : await request.arrayBuffer();\n\n for (let hop = 0; ; hop++) {\n if (hop === maxRedirects) {\n throw new TypeError(\"Failed to fetch: too many redirects\");\n }\n\n const response = await dispatch(\n body === undefined || request.method === \"GET\"\n ? new Request(request, { body: null })\n : new Request(request, { body }),\n );\n\n const location = response.headers.get(\"location\");\n\n if (!location || !redirectCodes.includes(response.status)) {\n return response;\n }\n\n const method =\n response.status === 303 ||\n ([301, 302].includes(response.status) && request.method === \"POST\")\n ? \"GET\"\n : request.method;\n\n request = new Request(new URL(location, request.url), {\n method,\n headers: request.headers,\n });\n }\n };\n};\n\nconst ssrTransport = backend ? createTransport(backend) : undefined;\n\nexport const transport = ssrTransport\n ? async (input, init) => {\n try {\n const response = await ssrTransport(input, init);\n if (response?.ok) {\n return response;\n }\n // the rethrow here needed cause ssrTransport does not throw on non-2xx responses\n throw new SSRFetchError([\n input,\n response,\n typeof response?.text === \"function\"\n ? await response.text()\n : response?.statusText,\n ]);\n } catch (error) {\n /**\n * Capture the fetch error at the transport level and stash it on the request store.\n * Some frameworks - Solid notably - swallow a rejecting loader and still emit a partial render tree.\n * Storing the error here keeps it observable regardless of how the framework handles the loader rejection.\n * */\n const storage = store.getStore();\n if (storage) {\n storage.error = error;\n }\n throw error;\n }\n }\n : undefined; // let fetch clients pick the transport\n\nclass SSRFetchError extends Error {\n constructor([input, response, message]) {\n const pathname = pathnameOf(input);\n const status = response.status ?? \"unknown\";\n super(`${pathname}: ${status} [ ${message} ]`.trim());\n this.name = \"SSRFetchError\";\n }\n}\n\nconst pathnameOf = (input) => {\n try {\n if (typeof input === \"string\") {\n return new URL(input, \"http://x\").pathname;\n }\n if (input instanceof URL) {\n return input.pathname;\n }\n if (input instanceof Request) {\n return new URL(input.url).pathname;\n }\n } catch {}\n return String(input);\n};\n";
311
208
  var unwrap_default$2 = "export type MaybeWrapped<T> = T;\nexport const unwrap = <T>(data: T) => data;\n";
312
209
  var factory_default$12 = defineGeneratorFactory((sourceFolder) => {
@@ -379,7 +276,7 @@ var factory_default$12 = defineGeneratorFactory((sourceFolder) => {
379
276
  }
380
277
  };
381
278
  return {
382
- async start() {
279
+ async seed() {
383
280
  for (const [file, template] of [["unwrap.ts", unwrap_default$2]]) await deployLibFile(createPath.lib(file), template, {});
384
281
  },
385
282
  async watch(entries, event) {
@@ -411,7 +308,8 @@ var package_default$9 = {
411
308
  type: "module",
412
309
  "private": true,
413
310
  name: "@kosmojs/h3-generator",
414
- version: "0.3.0",
311
+ version: "0.0.0",
312
+ description: "H3 backend integration for KosmoJS (internal)",
415
313
  author: "Slee Woo",
416
314
  license: "MIT",
417
315
  files: ["pkg/*"],
@@ -437,15 +335,15 @@ var app_default$1$10 = "import { H3, type Middleware } from \"h3\";\n\nimport ty
437
335
  var dev_default$1$3 = "import type { DevSetup } from \"@kosmojs/core/api\";\n\nexport const devSetup = (setup: DevSetup) => setup;\n";
438
336
  var errors_default$1$3 = "import { type H3Event, HTTPError } from \"h3\";\n\nimport { ValidationError } from \"@kosmojs/core/errors\";\n\ntype ErrorHandler = (\n error: any,\n event: H3Event,\n) => Promise<Response> | Response;\n\nexport type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;\n\nexport const errorHandlerFactory: ErrorHandlerFactory = (handler) => {\n // H3 wraps errors in its own HTTPError with the original error as `cause`\n return (error, event) => {\n return handler(\n error instanceof HTTPError\n ? error.cause instanceof ValidationError\n ? error.cause\n : error\n : error,\n event,\n );\n };\n};\n";
439
337
  var listener_default$2 = "import { createListener } from \"./server\";\n\nimport app from \"{{ createImport 'api' 'app' }}\";\n\n/**\n * Entry for the `dist/<folder>/api/listener.js` bundle.\n * Exposes this folder's API as a plain node:http listener, mounted by `dist/run.js`\n * next to the other folders. Nothing here listens on a port - `server.ts` does that.\n * */\nexport default createListener(app);\n";
440
- var parsers_default$2 = "import { type H3Event, readBody } from \"h3\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataTarget,\n} from \"@kosmojs/core\";\n\nexport const metaparsers: {\n [T in RequestMetadataTarget]: (event: H3Event) => unknown;\n} = {\n query(event) {\n return parseSearchParams(event.url);\n },\n\n headers(event) {\n return Object.fromEntries(event.req.headers);\n },\n\n cookies(event) {\n return parseCookies(Object.fromEntries(event.req.headers));\n },\n};\n\nexport const bodyparsers: {\n [T in RequestBodyTarget]: (event: H3Event) => Promise<unknown>;\n} = {\n json(event) {\n return event.req.json();\n },\n\n form(event) {\n return readBody(event, { type: \"formData\" });\n },\n\n raw(event) {\n return event.req.text();\n },\n};\n";
441
- var router_default$8 = "import { command } from \"virtual:kosmo/env\";\nimport type { Middleware } from \"h3\";\n\nimport type {\n RequestBodyTarget,\n RequestMetadataTarget,\n RequestValidationTarget,\n ValidationErrorEntry,\n} from \"@kosmojs/core\";\nimport {\n type CreateRouteMiddleware,\n createRoutes,\n type HTTPMethod,\n StateKey,\n} from \"@kosmojs/core/api\";\nimport { ValidationError } from \"@kosmojs/core/errors\";\n\nimport {\n type DefaultContext,\n type ParameterizedEvent,\n type ParameterizedMiddleware,\n use,\n} from \"../api\";\nimport { bodyparsers, metaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\n/**\n * Create route-level middleware stack that handles:\n * 1. Context extension - adds `event.bodyparser` (lazy, cached) and `event.validated` accessors\n * 2. Params validation - normalizes and validates URL params (including splat/numeric params)\n * 3. Request validation - validates query, headers, cookies, and body against schemas\n * 4. Response validation - validates outgoing response against defined variants\n *\n * Middleware are assigned named slots (e.g. \"validate:params\", \"validate:json\")\n * so they can be replaced by user-defined middleware in the stack.\n *\n * All validation errors are thrown as `ValidationError` instances,\n * caught and formatted by the global error handler middleware upstream.\n * */\nexport const createRouteMiddleware: CreateRouteMiddleware<\n ParameterizedMiddleware\n> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {\n const validationMiddleware = [\n /**\n * Extends H3 event with:\n *\n * - `event.metaparser[target]()` - lazy, cached meta parsers.\n * Each parser (query, headers, cookies) runs at most once per request;\n * subsequent calls return the cached result.\n *\n * - `event.bodyparser[target](opts?)` - lazy, cached body parsers.\n * Each parser (json, form, raw) runs at most once per request;\n * subsequent calls return the cached result.\n * This allows both user middleware/handlers and validators\n * to call the same parser without re-consuming the request stream.\n *\n * - `event.validated` - getter that returns all validated data collected so far\n * (params, query, headers, cookies, json etc.) as a plain object.\n *\n * Cache is stored on `event[StateKey]` (a Symbol-keyed Map) to keep it\n * hidden from public API surface and serialization.\n * */\n use(\n function useExtendContext(event, next) {\n if (!event[StateKey]) {\n // initialize per-request cache with empty params\n // (later populated by useValidateParams)\n event[StateKey] = new Map([[\"params\", {}]]);\n\n Object.defineProperty(event, \"metaparser\", {\n value: Object.entries(metaparsers).reduce<{\n [T in RequestMetadataTarget]?: () => unknown;\n }>((map, entry) => {\n const [target, parser] = entry as [\n RequestMetadataTarget,\n Function,\n ];\n map[target] = () => {\n if (!event[StateKey].has(target)) {\n event[StateKey].set(\n target,\n target === \"query\"\n ? normalizeSearchParams(\n parser(event),\n event.req.method === \"HEAD\"\n ? \"GET\"\n : event.req.method,\n )\n : parser(event),\n );\n }\n return event[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(event, \"bodyparser\", {\n value: Object.entries(bodyparsers).reduce<{\n [T in RequestBodyTarget]?: () => Promise<unknown>;\n }>((map, entry) => {\n const [target, parser] = entry as [RequestBodyTarget, Function];\n map[target] = async () => {\n if (!event[StateKey].has(target)) {\n event[StateKey].set(target, await parser(event));\n }\n return event[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(event, \"validated\", {\n get() {\n return Object.fromEntries(event[StateKey]);\n },\n enumerable: true,\n });\n }\n return next();\n },\n { slot: \"@extendContext\" },\n ) as never,\n\n /**\n * Normalize and validate URL params:\n * - Splat params (e.g. `/files{/*path}`) are split into arrays by \"/\"\n * - Numeric params are cast to Number (or array of Numbers for splat params)\n * - Non-splat, non-numeric params pass through as strings\n *\n * Validated params are stored in the cache so `event.validated.params`\n * reflects the normalized (and validated) values.\n * */\n use(\n function useValidateParams(event, next) {\n const normalizedParams = normalizeParams(event.url.pathname);\n validationSchemas.params?.validate(normalizedParams);\n event[StateKey].set(\"params\", normalizedParams);\n return next();\n },\n { slot: \"validate:params\" },\n ) as never,\n\n /**\n * Response validation - runs AFTER the handler (post-`next()`).\n *\n * Each response schema defines one or more variants, each with:\n * - expected status code\n * - optional content-type\n * - optional body schema\n *\n * All variants are checked; if at least one passes, validation succeeds.\n * If none pass, a ValidationError is thrown with collected errors from all variants.\n *\n * Activation rules:\n * - In dev/test mode: runs unless `runtimeValidation` is explicitly `false`\n * - In production: runs only if `runtimeValidation` is explicitly `true`\n *\n * Only attached to HTTP methods that have response schemas defined.\n * */\n use(\n async function useValidateResponse(event, next) {\n const variants =\n validationSchemas.response?.[\n event.req.method === \"HEAD\" ? \"GET\" : event.req.method\n ] || [];\n\n if (!Array.isArray(variants) || !variants.length) {\n return next();\n }\n\n // options are same for all variants\n const { runtimeValidation, customErrors } = variants[0];\n\n if (command === \"build\") {\n // production build - skip if undefined or explicitly set to false\n if (runtimeValidation === undefined || runtimeValidation === false) {\n return next();\n }\n } else {\n // dev mode - skip only if explicitly set to false\n if (runtimeValidation === false) {\n return next();\n }\n }\n\n // run all downstream middleware (including the route handler)\n const body = await next();\n\n /**\n * H3 builds the actual Response AFTER the middleware chain completes,\n * so at this point event.res only reflects what handlers set explicitly.\n * Reconstruct what will be sent instead: a returned Response is authoritative;\n * otherwise an unset status means 200, and the content type follows H3's\n * serialization rules for the returned value (string -> text, object -> JSON).\n * */\n const rawResponse = body instanceof Response ? body : undefined;\n\n const response: {\n status: number;\n contentType: string | null;\n body?: unknown;\n } = {\n status: rawResponse?.status ?? event.res.status ?? 200,\n contentType:\n rawResponse?.headers.get(\"Content-Type\") ??\n event.res.headers.get(\"Content-Type\") ??\n (typeof body === \"string\"\n ? \"text/plain\"\n : body === undefined || body === null\n ? null\n : \"application/json\"),\n };\n\n // validate only 2xx responses\n if (Math.floor(response.status / 100) !== 2) {\n return;\n }\n\n // Validate body only for JSON variants\n if (variants.some((e) => e.contentType?.includes(\"json\"))) {\n response.body = rawResponse\n ? await rawResponse\n .clone()\n .json()\n .catch(() => undefined)\n : body;\n }\n\n /**\n * Returns an array of validator functions for a single response variant.\n * Each validator checks one aspect (status, content-type, body)\n * and returns an error entry or undefined if the check passes.\n * */\n const variantValidators: (\n v: (typeof variants)[number],\n ) => Array<(i: number) => ValidationErrorEntry | undefined> = (\n schema,\n ) => {\n return [\n (i) => {\n return schema.status === response.status\n ? undefined\n : {\n keyword: \"Status\",\n path: `Variant #${i}`,\n message: `expected: ${schema.status}; actual: ${response.status}`,\n };\n },\n (i) => {\n if (\n !schema.contentType ||\n schema.contentType === response.contentType\n ) {\n return undefined;\n }\n return {\n keyword: \"ContentType\",\n path: `Variant #${i}`,\n message: `expected: ${schema.contentType}; actual: ${response.contentType}`,\n };\n },\n (i) => {\n if (!schema.check || \"body\" in response === false) {\n // no body schema or contentType is not JSON\n return;\n }\n return schema.check(response.body)\n ? undefined\n : {\n keyword: \"Body\",\n path: `Variant #${i}`,\n message: schema.errorMessage(response.body),\n };\n },\n ];\n };\n\n // collect errors across all variants; exit early if any variant passes\n const errors: Array<ValidationErrorEntry> = [];\n\n for (const [i, variant] of variants.entries()) {\n const variantErrors = variantValidators(variant).flatMap(\n (validator) => {\n const error = validator(i);\n return error ? [error] : [];\n },\n );\n if (!variantErrors.length) {\n // variant fully matched - response is valid\n return;\n }\n errors.push(...variantErrors);\n }\n\n const errorMessage = `The response did not match any of the expected formats`;\n const errorSummary = `${variants.length} variants checked, none valid`;\n\n // no variant passed validation\n throw new ValidationError([\n \"response\",\n {\n errors,\n errorMessage: customErrors?.error || errorMessage,\n errorSummary,\n route: name,\n data: response,\n },\n ]);\n },\n {\n slot: \"validate:response\",\n on: Object.keys(validationSchemas.response || {}) as Array<HTTPMethod>,\n },\n ) as never,\n ];\n\n /**\n * Request validation - dynamically creates one middleware per target\n * (query, headers, cookies, json, form, multipart, raw).\n *\n * Each middleware:\n * 1. Checks if a schema exists for the current HTTP method\n * 2. Skips if `runtimeValidation` is explicitly disabled\n * 3. Loads data via the appropriate source (event.query, event.headers, or event.bodyparser)\n * 4. Validates via `schema.validate()` which throws on failure\n *\n * Body targets (json, form, raw) go through `event.bodyparser[target]()`,\n * benefiting from the lazy parsing and caching set up by slot:extendContext middleware.\n *\n * All request validators are active on any HTTP method that has at least one\n * schema defined across any target - this is intentionally broad to avoid\n * silently skipping validation when methods overlap.\n * */\n const requestTargets: Record<\n RequestValidationTarget,\n (\n event: ParameterizedEvent<Record<string, string>, DefaultContext>,\n ) => Promise<unknown>\n > = {\n query: async (event) => event.metaparser.query(),\n headers: async (event) => event.metaparser.headers(),\n cookies: async (event) => event.metaparser.cookies(),\n json: async (event) => event.bodyparser.json(),\n form: async (event) => event.bodyparser.form(),\n raw: async (event) => event.bodyparser.raw(),\n };\n\n const requestEntries = Object.entries(requestTargets) as Array<\n [RequestValidationTarget, (typeof requestTargets)[RequestValidationTarget]]\n >;\n\n for (const [target, loadData] of requestEntries) {\n validationMiddleware.push(\n use(\n async (event, next) => {\n const schema = {\n ...validationSchemas[target]?.[\n event.req.method === \"HEAD\" ? \"GET\" : event.req.method\n ],\n };\n if (schema.validate && schema.runtimeValidation !== false) {\n schema.validate(await loadData(event as never));\n }\n return next();\n },\n {\n slot: `validate:${target}`,\n // duplicates not an issue here\n on: requestEntries.flatMap(([target]) => {\n return Object.keys(\n validationSchemas[target] || {},\n ) as Array<HTTPMethod>;\n }),\n },\n ) as never,\n );\n }\n\n return validationMiddleware;\n};\n\nexport const routes = createRoutes<ParameterizedMiddleware, Middleware>(\n routeSources,\n {\n globalMiddleware: globalMiddleware as never,\n createRouteMiddleware,\n },\n);\n";
442
- var routes_default$3 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", { ...{{serializeApiRoute .}}, pathPattern: \"{{aliasPattern}}\" }),\n path: \"{{alias}}\",\n pathPattern: \"{{alias}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n validationSchemas: {{id}}_schemas,\n },\n {{/each}}\n];\n";
338
+ var parsers_default$2 = "import { type H3Event, readBody } from \"h3\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataParser,\n} from \"@kosmojs/core\";\nimport { createParamsNormalizers, type RouteSource } from \"@kosmojs/core/api\";\n\nimport type { ParameterizedMiddleware } from \"../api\";\n\nexport const createMetaparsers: (\n r: RouteSource<ParameterizedMiddleware>,\n event: H3Event,\n) => Record<RequestMetadataParser, () => unknown> = (routeSource, event) => {\n const {\n //\n normalizeParams,\n normalizeSearchParams,\n } = createParamsNormalizers<ParameterizedMiddleware>(routeSource);\n\n return {\n method() {\n return event.req.method;\n },\n\n pathname() {\n return event.url.pathname;\n },\n\n params() {\n return normalizeParams(event.url.pathname);\n },\n\n query() {\n return normalizeSearchParams(\n parseSearchParams(event.url ?? \"\"),\n event.req.method,\n );\n },\n\n headers() {\n return Object.fromEntries(event.req.headers);\n },\n\n cookies() {\n return parseCookies(Object.fromEntries(event.req.headers));\n },\n };\n};\n\nexport const createBodyparsers: (\n r: RouteSource<ParameterizedMiddleware>,\n event: H3Event,\n) => Record<RequestBodyTarget, () => Promise<unknown>> = (\n _routeSource,\n event,\n) => {\n return {\n json() {\n return event.req.json();\n },\n\n form() {\n return readBody(event, { type: \"formData\" });\n },\n\n raw() {\n return event.req.text();\n },\n };\n};\n";
339
+ var router_default$8 = "import { command } from \"virtual:kosmo/env\";\nimport type { Middleware } from \"h3\";\n\nimport { createRoutes } from \"@kosmojs/core/api\";\n\nimport type { ParameterizedMiddleware } from \"../api\";\nimport { createBodyparsers, createMetaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\nexport const routes = createRoutes<ParameterizedMiddleware, Middleware>(\n routeSources,\n {\n productionBuild: command === \"build\",\n responseResolver(event, body) {\n /**\n * H3 builds the actual Response AFTER the middleware chain completes,\n * so at this point event.res only reflects what handlers set explicitly.\n * Reconstruct what will be sent instead: a returned Response is authoritative;\n * otherwise an unset status means 200, and the content type follows H3's\n * serialization rules for the returned value (string -> text, object -> JSON).\n * */\n const rawResponse = body instanceof Response ? body : undefined;\n\n return {\n status: rawResponse?.status ?? event.res.status ?? 200,\n contentType:\n rawResponse?.headers.get(\"Content-Type\") ??\n event.res.headers.get(\"Content-Type\") ??\n (typeof body === \"string\"\n ? \"text/plain\"\n : body === undefined || body === null\n ? null\n : \"application/json\"),\n async body() {\n return rawResponse\n ? await rawResponse\n .clone()\n .json()\n .catch(() => undefined)\n : body;\n },\n };\n },\n createBodyparsers,\n createMetaparsers,\n globalMiddleware: globalMiddleware as never,\n },\n);\n";
340
+ var routes_default$3 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", {\n ...{{serializeApiRoute .}},\n pathPattern: \"{{aliasPattern}}\",\n }),\n path: \"{{alias}}\",\n pathPattern: \"/{{aliasPattern}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n {{#if ../validationEnabled}}\n validationSchemas: {{id}}_schemas,\n {{/if}}\n },\n {{/each}}\n];\n";
443
341
  var server_default$1$10 = "import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { parseArgs, styleText } from \"node:util\";\n\nimport { serve as h3serve } from \"h3\";\nimport { toNodeHandler } from \"h3/node\";\n\nimport type { App } from \"./app\";\n\nexport type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;\n\n/**\n * Wrap the app into a node:http request listener.\n * Used by dist/run.js to mount this folder's API next to other folders;\n * the standalone server (`serve`) binds the app through h3's own adapter instead.\n * */\nexport const createListener = <T extends App>(app: T): NodeListener => {\n return toNodeHandler(app);\n};\n\ntype Handles = {\n port?: number | undefined;\n onListen?: () => Promise<void>;\n};\n\nconst getListenHandles = async (opt?: Handles) => {\n const { port } = opt\n ? opt\n : parseArgs({\n options: {\n port: {\n type: \"string\",\n short: \"p\",\n },\n },\n }).values;\n\n if (![port].some(Boolean)) {\n throw new Error(\"Please provide -p/--port number\");\n }\n\n const onListen = async () => {\n console.log(\n `\\n ✨ Server Started ${styleText([\"dim\"], \"[ %s ]\")}`,\n `port: ${port}`,\n );\n };\n\n return {\n port: Number(port),\n onListen: opt?.onListen || onListen,\n };\n};\n\nexport const serve = async <T extends App>(app: T, opt?: Handles) => {\n const { port, onListen } = await getListenHandles(opt);\n\n const server = h3serve(app, { port });\n await server.ready().then(onListen);\n\n return server as never;\n};\n";
444
- var api_default$2 = "import type { H3Event, H3EventContext } from \"h3\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultContext extends H3EventContext {}\n\ntype MaybePromise<T = unknown> = T | Promise<T>;\n\ntype Next = () => MaybePromise<unknown | undefined>;\n\ntype ExtractBodies<R> = R extends [number, string, infer Body] ? Body : never;\n\ntype ValidatedResponseBodies<VDefs extends ValidationDefmap> = [\n ExtractBodies<VDefs[\"response\"]>,\n] extends [never]\n ? unknown // No bodies extracted at all - fallback to unknown\n : ExtractBodies<VDefs[\"response\"]>;\n\nexport type ParameterizedEvent<\n ParamsT,\n ContextT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = H3Event & { context: DefaultContext } & ContextT &\n ExtendContext<ParamsT, VDefs, VOpts>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n ContextT = Record<string, unknown>,\n> = (\n event: ParameterizedEvent<ParamsT, ContextT>,\n next: Next,\n) => MaybePromise<unknown>;\n\nexport type RouteHandler<\n ParamsT,\n ContextT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n event: ParameterizedEvent<ParamsT, ContextT, VDefs, VOpts>,\n) => MaybePromise<ValidatedResponseBodies<VDefs>>;\n\nexport type DefineRouteFactory<ParamsT, ContextT> = (\n a: {\n // NOTE: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, ContextT>\n | Array<ParameterizedMiddleware<ParamsT, ContextT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, ContextT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, ContextT, VDefs, VOpts>>,\n ) => HandlerDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;\n },\n) => Array<RouteDefinitionItem<ParameterizedMiddleware<ParamsT, ContextT>>>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const use = <ContextT = DefaultContext>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, ContextT>\n | Array<ParameterizedMiddleware<Record<string, string>, ContextT>>,\n options?: UseOptions,\n) => {\n return createUse<ParameterizedMiddleware<Record<string, string>, ContextT>>(\n middleware,\n options,\n );\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n ContextT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n ContextT & RouteMap[R][\"cascadingState\"]\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n ContextT & RouteMap[R][\"cascadingState\"]\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
342
+ var api_default$2 = "import type { H3Event, H3EventContext } from \"h3\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type DefaultUseSlots,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultContext extends H3EventContext {}\nexport interface UseSlots extends DefaultUseSlots {}\n\ntype MaybePromise<T = unknown> = T | Promise<T>;\n\ntype Next = () => MaybePromise<unknown | undefined>;\n\ntype ExtractBodies<R> = R extends [number, string, infer Body] ? Body : never;\n\ntype ValidatedResponseBodies<VDefs extends ValidationDefmap> = [\n ExtractBodies<VDefs[\"response\"]>,\n] extends [never]\n ? unknown // No bodies extracted at all - fallback to unknown\n : ExtractBodies<VDefs[\"response\"]>;\n\nexport type ParameterizedEvent<\n ParamsT,\n ContextT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = H3Event & { context: DefaultContext } & ContextT &\n ExtendContext<ParamsT, VDefs, VOpts>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n ContextT = Record<string, unknown>,\n> = (\n event: ParameterizedEvent<ParamsT, ContextT>,\n next: Next,\n) => MaybePromise<unknown>;\n\nexport type RouteHandler<\n ParamsT,\n ContextT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n event: ParameterizedEvent<ParamsT, ContextT, VDefs, VOpts>,\n) => MaybePromise<ValidatedResponseBodies<VDefs>>;\n\nexport type DefineRouteFactory<ParamsT, ContextT> = (\n a: {\n // NOTE: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, ContextT>\n | Array<ParameterizedMiddleware<ParamsT, ContextT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, ContextT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, ContextT, VDefs, VOpts>>,\n ) => HandlerDefinition<ParameterizedMiddleware<ParamsT, ContextT>>;\n },\n) => Array<RouteDefinitionItem<ParameterizedMiddleware<ParamsT, ContextT>>>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const use = <ContextT = DefaultContext>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, ContextT>\n | Array<ParameterizedMiddleware<Record<string, string>, ContextT>>,\n options?: UseOptions<keyof UseSlots>,\n) => {\n return createUse<\n ParameterizedMiddleware<Record<string, string>, ContextT>,\n UseOptions<keyof UseSlots>\n >(middleware, options);\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n ContextT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n ContextT & RouteMap[R][\"cascadingState\"]\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n ContextT & RouteMap[R][\"cascadingState\"]\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
445
343
  var api_factory_default$2 = "export * from \"./@api/app\";\nexport { appFactory as default } from \"./@api/app\";\nexport * from \"./@api/dev\";\nexport * from \"./@api/errors\";\nexport * from \"./@api/router\";\nexport * from \"./@api/routes\";\nexport * from \"./@api/server\";\n";
446
344
  var app_default$11 = "import { onError } from \"h3\";\n\nimport appFactory, { routes } from \"{{ createImport 'lib' 'api:factory' }}\";\nimport defaultErrorHandler from \"./errors\";\n\nexport default appFactory(routes, ({ app }) => {\n app.use(onError(defaultErrorHandler));\n});\n";
447
345
  var dev_default$4 = "import { toNodeHandler } from \"h3/node\";\n\nimport app from \"./app\";\n\nimport { devSetup } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default devSetup({\n requestHandler() {\n return toNodeHandler(app);\n },\n teardownHandler() {\n // close db connections, server sockets etc.\n },\n});\n\nprocess.on(\"unhandledRejection\", (reason) => {\n console.error(\"💥 UNHANDLED REJECTION\");\n console.error(\"Reason:\", reason);\n process.exit(1);\n});\n";
448
- var env_d_default$7 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultContext {}\n}\n";
346
+ var env_d_default$7 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultContext {}\n interface UseSlots {}\n}\n";
449
347
  var errors_default$4 = "import { ValidationError } from \"@kosmojs/core/errors\";\nimport { HTTPError } from \"h3\";\n\nimport { errorHandlerFactory } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default errorHandlerFactory(async (error, event) => {\n const [status, message = \"Unknown error occurred\"] = Array.isArray(error)\n ? error\n : error instanceof HTTPError\n ? [error.status, error.message]\n : error instanceof ValidationError\n ? [400, `${error.target}: ${error.errorMessage}`]\n : [error.statusCode || 500, error.message];\n\n const accept = event.req.headers.get(\"accept\");\n\n return accept?.includes(\"application/json\")\n ? new Response(JSON.stringify({ error: message }), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n })\n : new Response(message, {\n status,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n});\n";
450
348
  var route_default$2 = "import { defineRoute } from \"{{ createImport 'libApi' }}\";\n\nexport default defineRoute<\"{{route.name}}\">(({ GET }) => [\n GET(async (event) => {\n return \"{{route.name}} route starts here - replace this response with real logic.\";\n }),\n]);\n";
451
349
  var use_default$1$3 = "import { use } from \"{{ createImport 'libApi' }}\";\n\nexport type UseT = {};\n\nexport default [\n use<UseT>(async (event, next) => {\n return next();\n }),\n];\n";
@@ -515,11 +413,12 @@ var factory_default$11 = defineGeneratorFactory((sourceFolder) => {
515
413
  }).sort(sortRoutes);
516
414
  for (const [file, template] of [["@api/routes.ts", routes_default$3]]) await deployLibFile(createPath.lib(file), template, {
517
415
  routes: routesWithAliases,
518
- cascadingMiddleware
416
+ cascadingMiddleware,
417
+ validationEnabled: sourceFolder.config.validation ? true : false
519
418
  });
520
419
  };
521
420
  return {
522
- async start() {
421
+ async seed() {
523
422
  for (const [file, template] of [
524
423
  ["api.ts", api_default$2],
525
424
  ["api:factory.ts", api_factory_default$2],
@@ -564,7 +463,8 @@ var package_default$8 = {
564
463
  type: "module",
565
464
  "private": true,
566
465
  name: "@kosmojs/hono-generator",
567
- version: "0.3.0",
466
+ version: "0.0.0",
467
+ description: "Hono backend integration for KosmoJS (internal)",
568
468
  author: "Slee Woo",
569
469
  license: "MIT",
570
470
  files: ["pkg/*"],
@@ -591,15 +491,15 @@ var app_default$1$9 = "import { Hono, type MiddlewareHandler } from \"hono\";\ni
591
491
  var dev_default$1$1 = "import type { DevSetup } from \"@kosmojs/core/api\";\n\nexport const devSetup = (setup: DevSetup) => setup;\n";
592
492
  var errors_default$1$1 = "import type { Context } from \"hono\";\n\nimport type { AppEnv } from \"./app\";\n\ntype ErrorHandler = (\n error: any,\n ctx: Context<AppEnv>,\n) => Promise<Response> | Response;\n\nexport type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;\n\nexport const errorHandlerFactory: ErrorHandlerFactory = (handler) => {\n return handler;\n};\n";
593
493
  var listener_default$1 = "import { createListener } from \"./server\";\n\nimport app from \"{{ createImport 'api' 'app' }}\";\n\n/**\n * Entry for the `dist/<folder>/api/listener.js` bundle.\n * Exposes this folder's API as a plain node:http listener, mounted by `dist/run.js`\n * next to the other folders. Nothing here listens on a port - `server.ts` does that.\n * */\nexport default createListener(app);\n";
594
- var parsers_default$1 = "import type { Context } from \"hono\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataTarget,\n} from \"@kosmojs/core\";\n\nexport type BodyparserOptions = {\n json: never;\n form: { all?: boolean; dot?: boolean };\n raw: { as?: \"text\" | \"arrayBuffer\" | \"blob\" | \"formData\" };\n};\n\nexport const metaparsers: {\n [T in RequestMetadataTarget]: (ctx: Context) => unknown;\n} = {\n query(ctx) {\n return parseSearchParams(ctx.req.url);\n },\n\n headers(ctx) {\n return Object.fromEntries(ctx.req.raw.headers);\n },\n\n cookies(ctx) {\n return parseCookies(Object.fromEntries(ctx.req.raw.headers));\n },\n};\n\nexport const bodyparsers: {\n [T in RequestBodyTarget]: (\n ctx: Context,\n opt?: BodyparserOptions[T],\n ) => Promise<unknown>;\n} = {\n json(ctx) {\n return ctx.req.json();\n },\n\n form(ctx, opt) {\n return ctx.req.parseBody(opt);\n },\n\n raw(ctx, { as = \"text\" } = {}) {\n return ctx.req[as]();\n },\n};\n";
595
- var router_default$6 = "import { command } from \"virtual:kosmo/env\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport type {\n RequestBodyTarget,\n RequestMetadataTarget,\n RequestValidationTarget,\n ValidationErrorEntry,\n} from \"@kosmojs/core\";\nimport {\n type CreateRouteMiddleware,\n createRoutes,\n type HTTPMethod,\n StateKey,\n} from \"@kosmojs/core/api\";\nimport { ValidationError } from \"@kosmojs/core/errors\";\n\nimport {\n type DefaultBindings,\n type DefaultVariables,\n type ParameterizedContext,\n type ParameterizedMiddleware,\n use,\n} from \"../api\";\nimport { type BodyparserOptions, bodyparsers, metaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\n/**\n * Create route-level middleware stack that handles:\n * 1. Context extension - adds `ctx.bodyparser` (lazy, cached) and `ctx.validated` accessors\n * 2. Params validation - normalizes and validates URL params (including splat/numeric params)\n * 3. Request validation - validates query, headers, cookies, and body against schemas\n * 4. Response validation - validates outgoing response against defined variants\n *\n * Middleware are assigned named slots (e.g. \"validate:params\", \"validate:json\")\n * so they can be replaced by user-defined middleware in the stack.\n *\n * All validation errors are thrown as `ValidationError` instances,\n * caught and formatted by the global error handler middleware upstream.\n * */\nexport const createRouteMiddleware: CreateRouteMiddleware<\n ParameterizedMiddleware\n> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {\n const validationMiddleware = [\n /**\n * Extends Hono context with:\n *\n * - `ctx.metaparser[target]()` - lazy, cached meta parsers.\n * Each parser (query, headers, cookies) runs at most once per request;\n * subsequent calls return the cached result.\n *\n * - `ctx.bodyparser[target](opts?)` - lazy, cached body parsers.\n * Each parser (json, form, raw) runs at most once per request;\n * subsequent calls return the cached result.\n * This allows both user middleware/handlers and validators\n * to call the same parser without re-consuming the request stream.\n *\n * - `ctx.validated` - getter that returns all validated data collected so far\n * (params, query, headers, cookies, json etc.) as a plain object.\n *\n * Cache is stored on `ctx[StateKey]` (a Symbol-keyed Map) to keep it\n * hidden from public API surface and serialization.\n * */\n use(\n function useExtendContext(ctx, next) {\n if (!ctx[StateKey]) {\n // initialize per-request cache with empty params\n // (later populated by useValidateParams)\n ctx[StateKey] = new Map([[\"params\", {}]]);\n\n Object.defineProperty(ctx, \"metaparser\", {\n value: Object.entries(metaparsers).reduce<{\n [T in RequestMetadataTarget]?: () => unknown;\n }>((map, entry) => {\n const [target, parser] = entry as [\n RequestMetadataTarget,\n Function,\n ];\n map[target] = () => {\n if (!ctx[StateKey].has(target)) {\n ctx[StateKey].set(\n target,\n target === \"query\"\n ? normalizeSearchParams(\n parser(ctx),\n ctx.req.method === \"HEAD\" ? \"GET\" : ctx.req.method,\n )\n : parser(ctx),\n );\n }\n return ctx[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(ctx, \"bodyparser\", {\n value: Object.entries(bodyparsers).reduce<{\n [T in RequestBodyTarget]?: (\n opt?: BodyparserOptions[T],\n ) => Promise<unknown>;\n }>((map, entry) => {\n const [target, parser] = entry as [RequestBodyTarget, Function];\n map[target] = async (opt) => {\n if (!ctx[StateKey].has(target)) {\n ctx[StateKey].set(target, await parser(ctx, opt as never));\n }\n return ctx[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(ctx, \"validated\", {\n get() {\n return Object.fromEntries(ctx[StateKey]);\n },\n enumerable: true,\n });\n }\n return next();\n },\n { slot: \"@extendContext\" },\n ) as never,\n\n /**\n * Normalize and validate URL params:\n * - Splat params (e.g. `/files{/*path}`) are split into arrays by \"/\"\n * - Numeric params are cast to Number (or array of Numbers for splat params)\n * - Non-splat, non-numeric params pass through as strings\n *\n * Validated params are stored in the cache so `ctx.validated.params`\n * reflects the normalized (and validated) values.\n * */\n use(\n function useValidateParams(ctx, next) {\n const normalizedParams = normalizeParams(ctx.req.path);\n validationSchemas.params?.validate(normalizedParams);\n ctx[StateKey].set(\"params\", normalizedParams);\n return next();\n },\n { slot: \"validate:params\" },\n ) as never,\n\n /**\n * Response validation - runs AFTER the handler (post-`next()`).\n *\n * Each response schema defines one or more variants, each with:\n * - expected status code\n * - optional content-type\n * - optional body schema\n *\n * All variants are checked; if at least one passes, validation succeeds.\n * If none pass, a ValidationError is thrown with collected errors from all variants.\n *\n * Activation rules:\n * - In dev/test mode: runs unless `runtimeValidation` is explicitly `false`\n * - In production: runs only if `runtimeValidation` is explicitly `true`\n *\n * Only attached to HTTP methods that have response schemas defined.\n * */\n use(\n async function useValidateResponse(ctx, next) {\n const variants =\n validationSchemas.response?.[\n ctx.req.method === \"HEAD\" ? \"GET\" : ctx.req.method\n ] || [];\n\n if (!Array.isArray(variants) || !variants.length) {\n return next();\n }\n\n // options are same for all variants\n const { runtimeValidation, customErrors } = variants[0];\n\n if (command === \"build\") {\n // production build - skip if undefined or explicitly set to false\n if (runtimeValidation === undefined || runtimeValidation === false) {\n return next();\n }\n } else {\n // dev mode - skip only if explicitly set to false\n if (runtimeValidation === false) {\n return next();\n }\n }\n\n // run all downstream middleware (including the route handler)\n await next();\n\n const response: {\n status: number;\n contentType: string | null;\n body?: unknown;\n } = {\n status: ctx.res.status,\n contentType: ctx.res.headers.get(\"Content-Type\"),\n };\n\n // validate only 2xx responses\n if (Math.floor(response.status / 100) !== 2) {\n return;\n }\n\n // Validate body only for JSON variants\n if (variants.some((e) => e.contentType?.includes(\"json\"))) {\n response.body = await ctx.res\n .clone()\n .json()\n .catch(() => undefined);\n }\n\n /**\n * Returns an array of validator functions for a single response variant.\n * Each validator checks one aspect (status, content-type, body)\n * and returns an error entry or undefined if the check passes.\n * */\n const variantValidators: (\n v: (typeof variants)[number],\n ) => Array<(i: number) => ValidationErrorEntry | undefined> = (\n schema,\n ) => {\n return [\n (i) => {\n return schema.status === response.status\n ? undefined\n : {\n keyword: \"Status\",\n path: `Variant #${i}`,\n message: `expected: ${schema.status}; actual: ${ctx.status}`,\n };\n },\n (i) => {\n if (\n !schema.contentType ||\n schema.contentType === response.contentType\n ) {\n return undefined;\n }\n return {\n keyword: \"ContentType\",\n path: `Variant #${i}`,\n message: `expected: ${schema.contentType}; actual: ${response.contentType}`,\n };\n },\n (i) => {\n if (!schema.check || \"body\" in response === false) {\n // no body schema or contentType is not JSON\n return;\n }\n return schema.check(response.body)\n ? undefined\n : {\n keyword: \"Body\",\n path: `Variant #${i}`,\n message: schema.errorMessage(response.body),\n };\n },\n ];\n };\n\n // collect errors across all variants; exit early if any variant passes\n const errors: Array<ValidationErrorEntry> = [];\n\n for (const [i, variant] of variants.entries()) {\n const variantErrors = variantValidators(variant).flatMap(\n (validator) => {\n const error = validator(i);\n return error ? [error] : [];\n },\n );\n if (!variantErrors.length) {\n // variant fully matched - response is valid\n return;\n }\n errors.push(...variantErrors);\n }\n\n const errorMessage = `The response did not match any of the expected formats`;\n const errorSummary = `${variants.length} variants checked, none valid`;\n\n // no variant passed validation\n throw new ValidationError([\n \"response\",\n {\n errors,\n errorMessage: customErrors?.error || errorMessage,\n errorSummary,\n route: name,\n data: response,\n },\n ]);\n },\n {\n slot: \"validate:response\",\n on: Object.keys(validationSchemas.response || {}) as Array<HTTPMethod>,\n },\n ) as never,\n ];\n\n /**\n * Request validation - dynamically creates one middleware per target\n * (query, headers, cookies, json, form, multipart, raw).\n *\n * Each middleware:\n * 1. Checks if a schema exists for the current HTTP method\n * 2. Skips if `runtimeValidation` is explicitly disabled\n * 3. Loads data via the appropriate source (ctx.query, ctx.headers, or ctx.bodyparser)\n * 4. Validates via `schema.validate()` which throws on failure\n *\n * Body targets (json, form, raw) go through `ctx.bodyparser[target]()`,\n * benefiting from the lazy parsing and caching set up by slot:extendContext middleware.\n *\n * All request validators are active on any HTTP method that has at least one\n * schema defined across any target - this is intentionally broad to avoid\n * silently skipping validation when methods overlap.\n * */\n const requestTargets: Record<\n RequestValidationTarget,\n (\n ctx: ParameterizedContext<\n Record<string, string>,\n DefaultVariables,\n DefaultBindings\n >,\n ) => Promise<unknown>\n > = {\n query: async (ctx) => ctx.metaparser.query(),\n headers: async (ctx) => ctx.metaparser.headers(),\n cookies: async (ctx) => ctx.metaparser.cookies(),\n json: async (ctx) => ctx.bodyparser.json(),\n form: async (ctx) => ctx.bodyparser.form(),\n raw: async (ctx) => ctx.bodyparser.raw(),\n };\n\n const requestEntries = Object.entries(requestTargets) as Array<\n [RequestValidationTarget, (typeof requestTargets)[RequestValidationTarget]]\n >;\n\n for (const [target, loadData] of requestEntries) {\n validationMiddleware.push(\n use(\n async (ctx, next) => {\n const schema = {\n ...validationSchemas[target]?.[\n ctx.req.method === \"HEAD\" ? \"GET\" : ctx.req.method\n ],\n };\n if (schema.validate && schema.runtimeValidation !== false) {\n schema.validate(await loadData(ctx as never));\n }\n return next();\n },\n {\n slot: `validate:${target}`,\n // duplicates not an issue here\n on: requestEntries.flatMap(([target]) => {\n return Object.keys(\n validationSchemas[target] || {},\n ) as Array<HTTPMethod>;\n }),\n },\n ) as never,\n );\n }\n\n return validationMiddleware;\n};\n\nexport const routes = createRoutes<ParameterizedMiddleware, MiddlewareHandler>(\n routeSources,\n {\n globalMiddleware: globalMiddleware as never,\n createRouteMiddleware,\n },\n);\n";
596
- var routes_default$2 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", { ...{{serializeApiRoute .}}, pathPattern: \"{{aliasPattern}}\" }),\n path: \"{{alias}}\",\n pathPattern: \"{{alias}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n validationSchemas: {{id}}_schemas,\n },\n {{/each}}\n];\n";
494
+ var parsers_default$1 = "import type { Context } from \"hono\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataParser,\n} from \"@kosmojs/core\";\nimport { createParamsNormalizers, type RouteSource } from \"@kosmojs/core/api\";\n\nimport type { ParameterizedMiddleware } from \"../api\";\n\nexport type BodyparserOptions = {\n json: never;\n form: { all?: boolean; dot?: boolean };\n raw: { as?: \"text\" | \"arrayBuffer\" | \"blob\" | \"formData\" };\n};\n\nexport const createMetaparsers: (\n r: RouteSource<ParameterizedMiddleware>,\n ctx: Context,\n) => Record<RequestMetadataParser, () => unknown> = (routeSource, ctx) => {\n const {\n //\n normalizeParams,\n normalizeSearchParams,\n } = createParamsNormalizers<ParameterizedMiddleware>(routeSource);\n\n return {\n method() {\n return ctx.req.method;\n },\n\n pathname() {\n return ctx.req.path;\n },\n\n params() {\n return normalizeParams(ctx.req.path);\n },\n\n query() {\n return normalizeSearchParams(\n parseSearchParams(ctx.req.url),\n ctx.req.method,\n );\n },\n\n headers() {\n return Object.fromEntries(ctx.req.raw.headers);\n },\n\n cookies() {\n return parseCookies(Object.fromEntries(ctx.req.raw.headers));\n },\n };\n};\n\nexport const createBodyparsers: (\n r: RouteSource<ParameterizedMiddleware>,\n ctx: Context,\n) => {\n [T in RequestBodyTarget]: (opt?: BodyparserOptions[T]) => Promise<unknown>;\n} = (_routeSource, ctx) => {\n return {\n json() {\n return ctx.req.json();\n },\n\n form(opt) {\n return ctx.req.parseBody(opt);\n },\n\n raw({ as = \"text\" } = {}) {\n return ctx.req[as]();\n },\n };\n};\n";
495
+ var router_default$6 = "import { command } from \"virtual:kosmo/env\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport { createRoutes } from \"@kosmojs/core/api\";\n\nimport type { ParameterizedMiddleware } from \"../api\";\nimport { createBodyparsers, createMetaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\nexport const routes = createRoutes<ParameterizedMiddleware, MiddlewareHandler>(\n routeSources,\n {\n productionBuild: command === \"build\",\n createBodyparsers,\n createMetaparsers,\n responseResolver(ctx) {\n return {\n status: ctx.res.status,\n contentType: ctx.res.headers.get(\"Content-Type\"),\n async body() {\n return ctx.res\n .clone()\n .json()\n .catch(() => undefined);\n },\n };\n },\n globalMiddleware: globalMiddleware as never,\n },\n);\n";
496
+ var routes_default$2 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", {\n ...{{serializeApiRoute .}},\n pathPattern: \"{{aliasPattern}}\",\n }),\n path: \"{{alias}}\",\n pathPattern: \"/{{aliasPattern}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n {{#if ../validationEnabled}}\n validationSchemas: {{id}}_schemas,\n {{/if}}\n },\n {{/each}}\n];\n";
597
497
  var server_default$1$9 = "import { chmod, unlink } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { parseArgs, styleText } from \"node:util\";\n\nimport { createAdaptorServer, getRequestListener } from \"@hono/node-server\";\n\nimport type { App } from \"./app\";\n\nexport type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;\n\n/**\n * Wrap the app into a node:http request listener.\n * Used by dist/run.js to mount this folder's API next to other folders;\n * the standalone server (`serve`) binds the app through the runtime's native adapter instead.\n * */\nexport const createListener = <T extends App>(app: T): NodeListener => {\n return getRequestListener(app.fetch);\n};\n\ntype Handles = {\n port?: number | undefined;\n sock?: string | undefined;\n onListen?: () => Promise<void>;\n};\n\nconst getListenHandles = async (opt?: Handles) => {\n const { port, sock } = opt\n ? opt\n : parseArgs({\n options: {\n port: {\n type: \"string\",\n short: \"p\",\n },\n sock: {\n type: \"string\",\n short: \"s\",\n },\n },\n }).values;\n\n if (![port, sock].some(Boolean)) {\n throw new Error(\"Please provide either -p/--port number or -s/--sock path\");\n }\n\n if (sock) {\n await unlink(sock).catch((error) => {\n if (error.code !== \"ENOENT\") {\n throw error;\n }\n });\n }\n\n const onListen = async () => {\n if (sock) {\n // Make Unix socket world-writable so other processes (e.g. a reverse proxy)\n // can connect without permission issues.\n await chmod(sock, 0o777);\n }\n console.log(\n `\\n ✨ Server Started ${styleText([\"dim\"], \"[ %s ]\")}`,\n sock ? `sock: ${sock}` : `port: ${port}`,\n );\n };\n\n return {\n port: Number(port),\n sock,\n onListen: opt?.onListen || onListen,\n };\n};\n\nexport const serve = async <T extends App>(app: T, opt?: Handles) => {\n const { port, sock, onListen } = await getListenHandles(opt);\n\n if (typeof Bun !== \"undefined\") {\n const server = Bun.serve(\n sock\n ? { unix: sock, fetch: app.fetch }\n : { port: Number(port), fetch: app.fetch },\n );\n await onListen();\n return server as never;\n }\n\n if (typeof Deno !== \"undefined\") {\n const server = sock\n ? Deno.serve({ path: sock, onListen }, app.fetch)\n : Deno.serve({ port: Number(port), onListen }, app.fetch);\n return server as never;\n }\n\n const server = createAdaptorServer(app);\n server.listen(sock || port, onListen);\n\n return server as never;\n};\n";
598
- var api_default$1 = "import type { Context, Next } from \"hono\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { BodyparserOptions } from \"./@api/parsers\";\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultVariables {}\nexport interface DefaultBindings {}\n\ntype ExtractJsonBody<R> = R extends [\n number,\n `${string}json${string}`,\n infer Body,\n]\n ? Body\n : never;\n\ntype ExtractJsonStatus<R> = R extends [\n infer S extends number,\n `${string}json${string}`,\n ...unknown[],\n]\n ? S extends 300 | 301 | 302 | 303 | 304 | 307 | 308\n ? never\n : S\n : never;\n\ntype ResponseJsonOverride<VDefs extends ValidationDefmap> = [\n ExtractJsonBody<VDefs[\"response\"]>,\n] extends [never]\n ? {}\n : {\n json: (\n body: ExtractJsonBody<VDefs[\"response\"]>,\n status?: ExtractJsonStatus<VDefs[\"response\"]>,\n ) => Response;\n };\n\nexport type ParameterizedContext<\n ParamsT,\n VariablesT,\n BindingsT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = Context<{\n Bindings: DefaultBindings & BindingsT;\n Variables: DefaultVariables & VariablesT;\n}> &\n ExtendContext<ParamsT, VDefs, VOpts, BodyparserOptions> &\n ResponseJsonOverride<VDefs>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n VariablesT = Record<string, unknown>,\n BindingsT = Record<string, unknown>,\n> = (\n ctx: ParameterizedContext<ParamsT, VariablesT, BindingsT>,\n next: Next,\n) => Promise<unknown> | unknown;\n\nexport const use = <VariablesT = DefaultVariables>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, VariablesT>\n | Array<ParameterizedMiddleware<Record<string, string>, VariablesT>>,\n options?: UseOptions,\n) => {\n return createUse<ParameterizedMiddleware<Record<string, string>, VariablesT>>(\n middleware,\n options,\n );\n};\n\nexport type RouteHandler<\n ParamsT,\n VariablesT,\n BindingsT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n ctx: ParameterizedContext<ParamsT, VariablesT, BindingsT, VDefs, VOpts>,\n next: Next,\n) => Promise<unknown> | unknown;\n\nexport type DefineRouteFactory<ParamsT, VariablesT, BindingsT> = (\n a: {\n // NOTE: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n | Array<ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<\n ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n >;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, VariablesT, BindingsT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, VariablesT, BindingsT, VDefs, VOpts>>,\n ) => HandlerDefinition<\n ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n >;\n },\n) => Array<\n RouteDefinitionItem<ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>>\n>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n VariablesT extends object = object,\n BindingsT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n VariablesT & RouteMap[R][\"cascadingState\"],\n BindingsT\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n VariablesT & RouteMap[R][\"cascadingState\"],\n BindingsT\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
498
+ var api_default$1 = "import type { Context, Next } from \"hono\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type DefaultUseSlots,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { BodyparserOptions } from \"./@api/parsers\";\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultVariables {}\nexport interface DefaultBindings {}\nexport interface UseSlots extends DefaultUseSlots {}\n\ntype ExtractJsonBody<R> = R extends [\n number,\n `${string}json${string}`,\n infer Body,\n]\n ? Body\n : never;\n\ntype ExtractJsonStatus<R> = R extends [\n infer S extends number,\n `${string}json${string}`,\n ...unknown[],\n]\n ? S extends 300 | 301 | 302 | 303 | 304 | 307 | 308\n ? never\n : S\n : never;\n\ntype ResponseJsonOverride<VDefs extends ValidationDefmap> = [\n ExtractJsonBody<VDefs[\"response\"]>,\n] extends [never]\n ? {}\n : {\n json: (\n body: ExtractJsonBody<VDefs[\"response\"]>,\n status?: ExtractJsonStatus<VDefs[\"response\"]>,\n ) => Response;\n };\n\nexport type ParameterizedContext<\n ParamsT,\n VariablesT,\n BindingsT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = Context<{\n Bindings: DefaultBindings & BindingsT;\n Variables: DefaultVariables & VariablesT;\n}> &\n ExtendContext<ParamsT, VDefs, VOpts, BodyparserOptions> &\n ResponseJsonOverride<VDefs>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n VariablesT = Record<string, unknown>,\n BindingsT = Record<string, unknown>,\n> = (\n ctx: ParameterizedContext<ParamsT, VariablesT, BindingsT>,\n next: Next,\n) => Promise<unknown> | unknown;\n\nexport const use = <VariablesT = DefaultVariables>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, VariablesT>\n | Array<ParameterizedMiddleware<Record<string, string>, VariablesT>>,\n options?: UseOptions<keyof UseSlots>,\n) => {\n return createUse<\n ParameterizedMiddleware<Record<string, string>, VariablesT>,\n UseOptions<keyof UseSlots>\n >(middleware, options);\n};\n\nexport type RouteHandler<\n ParamsT,\n VariablesT,\n BindingsT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n ctx: ParameterizedContext<ParamsT, VariablesT, BindingsT, VDefs, VOpts>,\n next: Next,\n) => Promise<unknown> | unknown;\n\nexport type DefineRouteFactory<ParamsT, VariablesT, BindingsT> = (\n a: {\n // NOTE: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n | Array<ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<\n ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n >;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, VariablesT, BindingsT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, VariablesT, BindingsT, VDefs, VOpts>>,\n ) => HandlerDefinition<\n ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>\n >;\n },\n) => Array<\n RouteDefinitionItem<ParameterizedMiddleware<ParamsT, VariablesT, BindingsT>>\n>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n VariablesT extends object = object,\n BindingsT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n VariablesT & RouteMap[R][\"cascadingState\"],\n BindingsT\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n VariablesT & RouteMap[R][\"cascadingState\"],\n BindingsT\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
599
499
  var api_factory_default$1 = "export * from \"./@api/app\";\nexport { appFactory as default } from \"./@api/app\";\nexport * from \"./@api/dev\";\nexport * from \"./@api/errors\";\nexport * from \"./@api/router\";\nexport * from \"./@api/routes\";\nexport * from \"./@api/server\";\n";
600
500
  var app_default$6 = "import appFactory, { routes } from \"{{ createImport 'lib' 'api:factory' }}\";\nimport defaultErrorHandler from \"./errors\";\n\nexport default appFactory(routes, ({ app }) => {\n app.onError(defaultErrorHandler);\n})\n";
601
501
  var dev_default$2 = "import { getRequestListener } from \"@hono/node-server\";\n\nimport app from \"./app\";\n\nimport { devSetup } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default devSetup({\n requestHandler() {\n return getRequestListener(app.fetch);\n },\n teardownHandler() {\n // close db connections, server sockets etc.\n },\n});\n\nprocess.on(\"unhandledRejection\", (reason) => {\n console.error(\"💥 UNHANDLED REJECTION\");\n console.error(\"Reason:\", reason);\n process.exit(1);\n});\n\n";
602
- var env_d_default$6 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultVariables {}\n interface DefaultBindings {}\n}\n";
502
+ var env_d_default$6 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultVariables {}\n interface DefaultBindings {}\n interface UseSlots {}\n}\n";
603
503
  var errors_default$2 = "import { accepts } from \"hono/accepts\";\nimport { HTTPException } from \"hono/http-exception\";\n\nimport { ValidationError, HTTPError } from \"@kosmojs/core/errors\";\n\nimport { errorHandlerFactory } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default errorHandlerFactory(async (error, ctx) => {\n // Let Hono's HTTPException handle its own response\n if (error instanceof HTTPException) {\n return error.getResponse();\n }\n\n const [status, message] = Array.isArray(error)\n ? error\n : error instanceof HTTPError\n ? [error.status, error.message]\n : error instanceof ValidationError\n ? [400, `${error.target}: ${error.errorMessage}`]\n : [error.statusCode || 500, error.message];\n\n // Respond based on what the client accepts\n const type = accepts(ctx, {\n header: \"Accept\",\n supports: [\"application/json\", \"text/plain\"],\n default: \"text/plain\",\n });\n\n return type === \"application/json\"\n ? ctx.json({ error: message }, status)\n : ctx.text(message, status);\n});\n";
604
504
  var route_default$1 = "import { defineRoute } from \"{{ createImport 'libApi' }}\";\n\nexport default defineRoute<\"{{route.name}}\">(({ GET }) => [\n GET(async (ctx) => {\n // Always `return` the response!\n // ❗ Never call `ctx.json()` / `ctx.text()` / `ctx.body()` without returning!\n return ctx.text(\"{{route.name}} route starts here - replace this response with real logic.\");\n }),\n]);\n";
605
505
  var use_default$1$2 = "import { use } from \"{{ createImport 'libApi' }}\";\n\nexport type UseT = {};\n\nexport default [\n use<UseT>(async (ctx, next) => {\n // Always `return next()` or `await next()`;\n // ❗ Never call `next()` by itself!\n return next();\n }),\n];\n";
@@ -669,11 +569,12 @@ var factory_default$10 = defineGeneratorFactory((sourceFolder) => {
669
569
  }).sort(sortRoutes);
670
570
  for (const [file, template] of [["@api/routes.ts", routes_default$2]]) await deployLibFile(createPath.lib(file), template, {
671
571
  routes: routesWithAliases,
672
- cascadingMiddleware
572
+ cascadingMiddleware,
573
+ validationEnabled: sourceFolder.config.validation ? true : false
673
574
  });
674
575
  };
675
576
  return {
676
- async start() {
577
+ async seed() {
677
578
  for (const [file, template] of [
678
579
  ["api.ts", api_default$1],
679
580
  ["api:factory.ts", api_factory_default$1],
@@ -721,7 +622,8 @@ var package_default$7 = {
721
622
  type: "module",
722
623
  "private": true,
723
624
  name: "@kosmojs/koa-generator",
724
- version: "0.3.0",
625
+ version: "0.0.0",
626
+ description: "Koa backend integration for KosmoJS (internal)",
725
627
  author: "Slee Woo",
726
628
  license: "MIT",
727
629
  files: ["pkg/*"],
@@ -761,15 +663,15 @@ var app_default$1$8 = "import { styleText } from \"node:util\";\n\nimport Router
761
663
  var dev_default$1 = "import type { DevSetup } from \"@kosmojs/core/api\";\n\nexport const devSetup = (setup: DevSetup) => setup;\n";
762
664
  var errors_default$1 = "import type {\n DefaultContext,\n DefaultState,\n ParameterizedContext,\n} from \"../api\";\n\ntype ErrorHandler = (\n ctx: ParameterizedContext<unknown, DefaultState, DefaultContext>,\n next: Function,\n) => Promise<void>;\n\nexport type ErrorHandlerFactory = (handler: ErrorHandler) => ErrorHandler;\n\nexport const errorHandlerFactory: ErrorHandlerFactory = (handler) => {\n return handler;\n};\n";
763
665
  var listener_default = "import { createListener } from \"./server\";\n\nimport app from \"{{ createImport 'api' 'app' }}\";\n\n/**\n * Entry for the `dist/<folder>/api/listener.js` bundle.\n * Exposes this folder's API as a plain node:http listener, mounted by `dist/run.js`\n * next to the other folders. Nothing here listens on a port - `server.ts` does that.\n * */\nexport default createListener(app);\n";
764
- var parsers_default = "import zlib from \"node:zlib\";\n\nimport type { RouterContext } from \"@koa/router\";\nimport Formidable, { type Options as FormidableOptions } from \"formidable\";\nimport rawParser from \"raw-body\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataTarget,\n} from \"@kosmojs/core\";\n\nimport type {\n DefaultContext,\n DefaultState,\n ParameterizedContext,\n} from \"../api\";\n\nexport const metaparsers: {\n [T in RequestMetadataTarget]: (ctx: RouterContext) => unknown;\n} = {\n query(ctx) {\n return parseSearchParams(ctx.req.url ?? \"\");\n },\n\n headers(ctx) {\n return ctx.req.headers;\n },\n\n cookies(ctx) {\n return parseCookies(ctx.req.headers);\n },\n};\n\ntype JsonOptions = {\n limit?: number;\n};\n\n/**\n * Controls whether parsed field values are unwrapped from arrays.\n *\n * By default, all field values are returned as arrays (formidable v3 behavior),\n * since forms can submit multiple values under the same field name.\n *\n * - `false` (default) - no unwrapping, all values remain as arrays\n * - `true` - unwrap all fields; single-element arrays become scalars\n * - `{ only: string[] }` - unwrap only the specified fields\n * - `{ except: string[] }` - unwrap all fields except the specified ones\n *\n * Fields with multiple values are always kept as arrays regardless of this option.\n * If both `only` and `except` are provided, `only` takes precedence.\n *\n * @example\n * // all fields unwrapped: { username: 'john', role: 'admin' }\n * unwrap: true\n *\n * @example\n * // keep tags as array: { username: 'john', tags: ['a', 'b'] }\n * unwrap: { except: ['tags'] }\n *\n * @default false\n * */\ntype UnwrapControl =\n | boolean\n | {\n // unwrap only these keys\n only?: Array<string>;\n // unwrap all keys xcept these ones\n except?: Array<string>;\n };\n\ntype FormOptions = Pick<FormidableOptions, \"encoding\" | \"maxFields\"> &\n Partial<{\n // alias for maxFieldsSize\n limit: number;\n unwrap: UnwrapControl | undefined;\n }>;\n\ntype MultipartOptions = Omit<FormOptions, \"enabledPlugins\"> &\n Partial<{\n // alias for maxFieldsSize and maxFileSize\n limit: number;\n unwrap: UnwrapControl | undefined;\n }>;\n\ntype RawOptions = Partial<{\n /**\n * The byte limit of the body.\n * If the body ends up being larger than this limit, a 413 error code is returned.\n * */\n limit: number;\n\n /**\n * The length of the stream.\n * If the contents of the stream do not add up to this length,\n * an 400 error code is returned\n * */\n length: number;\n\n /**\n * The encoding to use to decode the body into a string.\n * By default, a Buffer instance will be returned when no encoding is specified.\n * utf-8 would decode as plain text.\n * use any encoding supported by iconv-lite.\n * */\n encoding: string;\n\n /**\n * zlib options\n * */\n chunkSize: number; // Default: 16 * 1024\n}>;\n\nexport const defaults: {\n json: JsonOptions;\n form: FormOptions | MultipartOptions;\n raw: RawOptions;\n} = {\n json: {\n limit: 1024 ** 2,\n },\n form: {\n limit: 1024 ** 2,\n },\n raw: {\n limit: 1024 ** 2,\n },\n};\n\nexport type BodyparserOptions = {\n json: JsonOptions;\n form: FormOptions | MultipartOptions;\n raw: RawOptions;\n};\n\nconst unwrap = (\n obj: Record<string, unknown | Array<unknown>>,\n opt: UnwrapControl | undefined,\n) => {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => {\n if (!Array.isArray(v) || v.length > 1) {\n return [k, v];\n }\n if (opt === false) {\n return [k, v];\n }\n if (opt === undefined || opt === true) {\n return [k, v[0]];\n }\n const { only, except } = { ...opt } as Exclude<UnwrapControl, boolean>;\n if (only?.includes(k)) {\n return [k, v[0]];\n }\n return [k, !except || except.includes(k) ? v : v[0]];\n }),\n );\n};\n\nexport const bodyparsers: {\n [T in RequestBodyTarget]: (\n ctx: ParameterizedContext<\n Record<string, string>,\n DefaultState,\n DefaultContext\n >,\n opt?: BodyparserOptions[T],\n ) => Promise<unknown>;\n} = {\n async json(ctx, opt) {\n const body = await bodyparsers.raw(ctx, {\n ...opt,\n encoding: \"utf-8\",\n });\n return body ? JSON.parse(body as never) : undefined;\n },\n\n async form(ctx, opt) {\n const form = Formidable({\n maxFieldsSize: opt?.limit || defaults.form.limit,\n maxFileSize: opt?.limit || defaults.form.limit,\n ...opt,\n });\n return new Promise((resolve, reject) => {\n form.parse(ctx.request.req, (err, fields, files) => {\n if (err) {\n return reject(err);\n }\n resolve(\n unwrap(\n // files should go last to override fields in case of name conflicting\n { ...fields, ...files },\n opt?.unwrap,\n ),\n );\n });\n });\n },\n\n async raw(ctx, opt) {\n const { chunkSize, ...rawParserOptions } = { ...defaults.raw, ...opt };\n\n const encoding = ctx.request.headers[\"content-encoding\"];\n const compressed = encoding ? encoding !== \"identity\" : false;\n\n const stream = compressed\n ? ctx.request.req.pipe(zlib.createUnzip({ chunkSize }))\n : ctx.request.req;\n\n return rawParser(stream, rawParserOptions);\n },\n};\n";
765
- var router_default$5 = "import { command } from \"virtual:kosmo/env\";\nimport type { RouterMiddleware } from \"@koa/router\";\n\nimport type {\n RequestBodyTarget,\n RequestMetadataTarget,\n RequestValidationTarget,\n ValidationErrorEntry,\n} from \"@kosmojs/core\";\nimport {\n type CreateRouteMiddleware,\n createRoutes,\n type HTTPMethod,\n StateKey,\n} from \"@kosmojs/core/api\";\nimport { ValidationError } from \"@kosmojs/core/errors\";\n\nimport {\n type DefaultContext,\n type DefaultState,\n type ParameterizedContext,\n type ParameterizedMiddleware,\n use,\n} from \"../api\";\nimport { type BodyparserOptions, bodyparsers, metaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\n/**\n * Create route-level middleware stack that handles:\n * 1. Context extension - adds `ctx.bodyparser` (lazy, cached) and `ctx.validated` accessors\n * 2. Params validation - normalizes and validates URL params (including splat/numeric params)\n * 3. Request validation - validates query, headers, cookies, and body against schemas\n * 4. Response validation - validates outgoing response against defined variants\n *\n * Middleware are assigned named slots (e.g. \"validate:params\", \"validate:json\")\n * so they can be replaced by user-defined middleware in the stack.\n *\n * All validation errors are thrown as `ValidationError` instances,\n * caught and formatted by the global error handler middleware upstream.\n * */\nexport const createRouteMiddleware: CreateRouteMiddleware<\n ParameterizedMiddleware\n> = ({ name, validationSchemas, normalizeParams, normalizeSearchParams }) => {\n const validationMiddleware = [\n /**\n * Extends Koa context with:\n *\n * - `ctx.metaparser[target]()` - lazy, cached meta parsers.\n * Each parser (query, headers, cookies) runs at most once per request;\n * subsequent calls return the cached result.\n *\n * - `ctx.bodyparser[target](opts?)` - lazy, cached body parsers.\n * Each parser (json, form, raw) runs at most once per request;\n * subsequent calls return the cached result.\n * This allows both user middleware/handlers and validators\n * to call the same parser without re-consuming the request stream.\n *\n * - `ctx.validated` - getter that returns all validated data collected so far\n * (params, query, headers, cookies, json etc.) as a plain object.\n *\n * Cache is stored on `ctx[StateKey]` (a Symbol-keyed Map) to keep it\n * hidden from public API surface and serialization.\n * */\n use(\n function useExtendContext(ctx, next) {\n if (!ctx[StateKey]) {\n // initialize per-request cache with empty params\n // (later populated by useValidateParams)\n ctx[StateKey] = new Map([[\"params\", {}]]);\n\n Object.defineProperty(ctx, \"metaparser\", {\n value: Object.entries(metaparsers).reduce<{\n [T in RequestMetadataTarget]?: () => unknown;\n }>((map, entry) => {\n const [target, parser] = entry as [\n RequestMetadataTarget,\n Function,\n ];\n map[target] = () => {\n if (!ctx[StateKey].has(target)) {\n ctx[StateKey].set(\n target,\n target === \"query\"\n ? normalizeSearchParams(\n parser(ctx),\n ctx.method === \"HEAD\" ? \"GET\" : ctx.method,\n )\n : parser(ctx),\n );\n }\n return ctx[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(ctx, \"bodyparser\", {\n value: Object.entries(bodyparsers).reduce<{\n [T in RequestBodyTarget]?: (\n opt?: BodyparserOptions[T],\n ) => Promise<unknown>;\n }>((map, entry) => {\n const [target, parser] = entry as [RequestBodyTarget, Function];\n map[target] = async (opt) => {\n if (!ctx[StateKey].has(target)) {\n ctx[StateKey].set(target, await parser(ctx, opt as never));\n }\n return ctx[StateKey].get(target);\n };\n return map;\n }, {}),\n enumerable: true,\n });\n\n Object.defineProperty(ctx, \"validated\", {\n get() {\n return Object.fromEntries(ctx[StateKey]);\n },\n enumerable: true,\n });\n }\n return next();\n },\n { slot: \"@extendContext\" },\n ) as never,\n\n /**\n * Normalize and validate URL params:\n * - Splat params (e.g. `/files{/*path}`) are split into arrays by \"/\"\n * - Numeric params are cast to Number (or array of Numbers for splat params)\n * - Non-splat, non-numeric params pass through as strings\n *\n * Validated params are stored in the cache so `ctx.validated.params`\n * reflects the normalized (and validated) values.\n * */\n use(\n function useValidateParams(ctx, next) {\n const normalizedParams = normalizeParams(ctx.path);\n validationSchemas.params?.validate(normalizedParams);\n ctx[StateKey].set(\"params\", normalizedParams);\n return next();\n },\n { slot: \"validate:params\" },\n ) as never,\n\n /**\n * Response validation - runs AFTER the handler (post-`next()`).\n *\n * Each response schema defines one or more variants, each with:\n * - expected status code\n * - optional content-type\n * - optional body schema\n *\n * All variants are checked; if at least one passes, validation succeeds.\n * If none pass, a ValidationError is thrown with collected errors from all variants.\n *\n * Activation rules:\n * - In dev/test mode: runs unless `runtimeValidation` is explicitly `false`\n * - In production: runs only if `runtimeValidation` is explicitly `true`\n *\n * Only attached to HTTP methods that have response schemas defined.\n * */\n use(\n async function useValidateResponse(ctx, next) {\n const variants =\n validationSchemas.response?.[\n ctx.method === \"HEAD\" ? \"GET\" : ctx.method\n ] || [];\n\n if (!Array.isArray(variants) || !variants.length) {\n return next();\n }\n\n // options are same for all variants\n const { runtimeValidation, customErrors } = variants[0];\n\n if (command === \"build\") {\n // production build - skip if undefined or explicitly set to false\n if (runtimeValidation === undefined || runtimeValidation === false) {\n return next();\n }\n } else {\n // dev mode - skip only if explicitly set to false\n if (runtimeValidation === false) {\n return next();\n }\n }\n\n // run all downstream middleware (including the route handler)\n // so ctx.status / ctx.type / ctx.body are set before we validate the response\n await next();\n\n const response: {\n status: number;\n contentType: string | null;\n body?: unknown;\n } = {\n status: ctx.status,\n contentType: ctx.type,\n };\n\n // validate only 2xx responses\n if (Math.floor(response.status / 100) !== 2) {\n return;\n }\n\n // Validate body only for JSON variants\n if (variants.some((e) => e.contentType?.includes(\"json\"))) {\n response.body = ctx.body;\n }\n\n /**\n * Returns an array of validator functions for a single response variant.\n * Each validator checks one aspect (status, content-type, body)\n * and returns an error entry or undefined if the check passes.\n * */\n const variantValidators: (\n v: (typeof variants)[number],\n ) => Array<(i: number) => ValidationErrorEntry | undefined> = (\n schema,\n ) => {\n return [\n (i) => {\n return schema.status === response.status\n ? undefined\n : {\n keyword: \"Status\",\n path: `Variant #${i}`,\n message: `expected: ${schema.status}; actual: ${response.status}`,\n };\n },\n (i) => {\n if (\n !schema.contentType ||\n schema.contentType === response.contentType\n ) {\n return undefined;\n }\n\n return {\n keyword: \"ContentType\",\n path: `Variant #${i}`,\n message: `expected: ${schema.contentType}; actual: ${response.contentType}`,\n };\n },\n (i) => {\n if (!schema.check || \"body\" in response === false) {\n // no body schema or contentType is not JSON\n return;\n }\n return schema.check(response.body)\n ? undefined\n : {\n keyword: \"Body\",\n path: `Variant #${i}`,\n message: schema.errorMessage(response.body),\n };\n },\n ];\n };\n\n // collect errors across all variants; exit early if any variant passes\n const errors: Array<ValidationErrorEntry> = [];\n\n for (const [i, variant] of variants.entries()) {\n const variantErrors = variantValidators(variant).flatMap(\n (validator) => {\n const error = validator(i);\n return error ? [error] : [];\n },\n );\n if (!variantErrors.length) {\n // variant fully matched - response is valid\n return;\n }\n errors.push(...variantErrors);\n }\n\n const errorMessage = `The response did not match any of the expected formats`;\n const errorSummary = `${variants.length} variants checked, none valid`;\n\n // no variant passed validation\n throw new ValidationError([\n \"response\",\n {\n errors,\n errorMessage: customErrors?.error || errorMessage,\n errorSummary,\n route: name,\n data: response,\n },\n ]);\n },\n {\n slot: \"validate:response\",\n on: Object.keys(validationSchemas.response || {}) as Array<HTTPMethod>,\n },\n ) as never,\n ];\n\n /**\n * Request validation - dynamically creates one middleware per target\n * (query, headers, cookies, json, form, raw).\n *\n * Each middleware:\n * 1. Checks if a schema exists for the current HTTP method\n * 2. Skips if `runtimeValidation` is explicitly disabled\n * 3. Loads data via the appropriate source (ctx.query, ctx.headers, or ctx.bodyparser)\n * 4. Validates via `schema.validate()` which throws on failure\n *\n * Body targets (json, form, raw) go through `ctx.bodyparser[target]()`,\n * benefiting from the lazy parsing and caching set up by slot:extendContext middleware.\n *\n * All request validators are active on any HTTP method that has at least one\n * schema defined across any target - this is intentionally broad to avoid\n * silently skipping validation when methods overlap.\n * */\n const requestTargets: Record<\n RequestValidationTarget,\n (\n ctx: ParameterizedContext<\n Record<string, string>,\n DefaultState,\n DefaultContext\n >,\n ) => Promise<unknown>\n > = {\n query: async (ctx) => ctx.metaparser.query(),\n headers: async (ctx) => ctx.metaparser.headers(),\n cookies: async (ctx) => ctx.metaparser.cookies(),\n json: async (ctx) => ctx.bodyparser.json(),\n form: async (ctx) => ctx.bodyparser.form(),\n raw: async (ctx) => ctx.bodyparser.raw(),\n };\n\n const requestEntries = Object.entries(requestTargets) as Array<\n [RequestValidationTarget, (typeof requestTargets)[RequestValidationTarget]]\n >;\n\n for (const [target, loadData] of requestEntries) {\n validationMiddleware.push(\n use(\n async (ctx, next) => {\n const schema = {\n ...validationSchemas[target]?.[\n ctx.method === \"HEAD\" ? \"GET\" : ctx.method\n ],\n };\n if (schema.validate && schema.runtimeValidation !== false) {\n schema.validate(await loadData(ctx as never));\n }\n return next();\n },\n {\n slot: `validate:${target}`,\n // duplicates not an issue here\n on: requestEntries.flatMap(([target]) => {\n return Object.keys(\n validationSchemas[target] || {},\n ) as Array<HTTPMethod>;\n }),\n },\n ) as never,\n );\n }\n\n return validationMiddleware;\n};\n\nexport const routes = createRoutes<ParameterizedMiddleware, RouterMiddleware>(\n routeSources,\n {\n globalMiddleware: globalMiddleware as never,\n createRouteMiddleware,\n },\n);\n";
766
- var routes_default$1 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", { ...{{serializeApiRoute .}}, pathPattern: \"{{aliasPattern}}\" }),\n path: \"{{alias}}\",\n pathPattern: \"{{alias}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n validationSchemas: {{id}}_schemas,\n },\n {{/each}}\n];\n";
666
+ var parsers_default = "import zlib from \"node:zlib\";\n\nimport Formidable, { type Options as FormidableOptions } from \"formidable\";\nimport rawParser from \"raw-body\";\n\nimport {\n parseCookies,\n parseSearchParams,\n type RequestBodyTarget,\n type RequestMetadataParser,\n} from \"@kosmojs/core\";\nimport { createParamsNormalizers, type RouteSource } from \"@kosmojs/core/api\";\n\nimport type {\n DefaultContext,\n DefaultState,\n ParameterizedContext,\n} from \"../api\";\n\nimport type { ParameterizedMiddleware } from \"{{ createImport 'libApi' }}\";\n\ntype Ctx = ParameterizedContext<\n Record<string, string>,\n DefaultState,\n DefaultContext\n>;\n\nexport const createMetaparsers: (\n routeSource: RouteSource<ParameterizedMiddleware>,\n ctx: Ctx,\n) => Record<RequestMetadataParser, () => unknown> = (routeSource, ctx) => {\n const {\n //\n normalizeParams,\n normalizeSearchParams,\n } = createParamsNormalizers<ParameterizedMiddleware>(routeSource);\n\n return {\n method() {\n return ctx.method;\n },\n\n pathname() {\n return ctx.path;\n },\n\n params() {\n return normalizeParams(ctx.path);\n },\n\n query() {\n return normalizeSearchParams(\n parseSearchParams(ctx.req.url ?? \"\"),\n ctx.method,\n );\n },\n\n headers() {\n return ctx.req.headers;\n },\n\n cookies() {\n return parseCookies(ctx.req.headers);\n },\n };\n};\n\ntype JsonOptions = {\n limit?: number;\n};\n\n/**\n * Controls whether parsed field values are unwrapped from arrays.\n *\n * By default, all field values are returned as arrays (formidable v3 behavior),\n * since forms can submit multiple values under the same field name.\n *\n * - `false` (default) - no unwrapping, all values remain as arrays\n * - `true` - unwrap all fields; single-element arrays become scalars\n * - `{ only: string[] }` - unwrap only the specified fields\n * - `{ except: string[] }` - unwrap all fields except the specified ones\n *\n * Fields with multiple values are always kept as arrays regardless of this option.\n * If both `only` and `except` are provided, `only` takes precedence.\n *\n * @example\n * // all fields unwrapped: { username: 'john', role: 'admin' }\n * unwrap: true\n *\n * @example\n * // keep tags as array: { username: 'john', tags: ['a', 'b'] }\n * unwrap: { except: ['tags'] }\n *\n * @default false\n * */\ntype UnwrapControl =\n | boolean\n | {\n // unwrap only these keys\n only?: Array<string>;\n // unwrap all keys xcept these ones\n except?: Array<string>;\n };\n\ntype FormOptions = Pick<FormidableOptions, \"encoding\" | \"maxFields\"> &\n Partial<{\n // alias for maxFieldsSize\n limit: number;\n unwrap: UnwrapControl | undefined;\n }>;\n\ntype MultipartOptions = Omit<FormOptions, \"enabledPlugins\"> &\n Partial<{\n // alias for maxFieldsSize and maxFileSize\n limit: number;\n unwrap: UnwrapControl | undefined;\n }>;\n\ntype RawOptions = Partial<{\n /**\n * The byte limit of the body.\n * If the body ends up being larger than this limit, a 413 error code is returned.\n * */\n limit: number;\n\n /**\n * The length of the stream.\n * If the contents of the stream do not add up to this length,\n * an 400 error code is returned\n * */\n length: number;\n\n /**\n * The encoding to use to decode the body into a string.\n * By default, a Buffer instance will be returned when no encoding is specified.\n * utf-8 would decode as plain text.\n * use any encoding supported by iconv-lite.\n * */\n encoding: string;\n\n /**\n * zlib options\n * */\n chunkSize: number; // Default: 16 * 1024\n}>;\n\nexport const defaults: {\n json: JsonOptions;\n form: FormOptions | MultipartOptions;\n raw: RawOptions;\n} = {\n json: {\n limit: 1024 ** 2,\n },\n form: {\n limit: 1024 ** 2,\n },\n raw: {\n limit: 1024 ** 2,\n },\n};\n\nexport type BodyparserOptions = {\n json: JsonOptions;\n form: FormOptions | MultipartOptions;\n raw: RawOptions;\n};\n\nconst unwrap = (\n obj: Record<string, unknown | Array<unknown>>,\n opt: UnwrapControl | undefined,\n) => {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => {\n if (!Array.isArray(v) || v.length > 1) {\n return [k, v];\n }\n if (opt === false) {\n return [k, v];\n }\n if (opt === undefined || opt === true) {\n return [k, v[0]];\n }\n const { only, except } = { ...opt } as Exclude<UnwrapControl, boolean>;\n if (only?.includes(k)) {\n return [k, v[0]];\n }\n return [k, !except || except.includes(k) ? v : v[0]];\n }),\n );\n};\n\nexport const createBodyparsers: (\n routeSource: RouteSource<ParameterizedMiddleware>,\n ctx: Ctx,\n) => {\n [T in RequestBodyTarget]: (opt?: BodyparserOptions[T]) => Promise<unknown>;\n} = (_routeSource, ctx) => {\n const raw = async (opt: any): Promise<unknown> => {\n const { chunkSize, ...rawParserOptions } = { ...defaults.raw, ...opt };\n\n const encoding = ctx.request.headers[\"content-encoding\"];\n const compressed = encoding ? encoding !== \"identity\" : false;\n\n const stream = compressed\n ? ctx.request.req.pipe(zlib.createUnzip({ chunkSize }))\n : ctx.request.req;\n\n // raw-body v4 returns a Promise if no callback provided\n return rawParser(stream, rawParserOptions);\n };\n\n return {\n async json(opt) {\n const body = await raw({\n ...opt,\n encoding: \"utf-8\",\n });\n return body ? JSON.parse(body as never) : undefined;\n },\n\n async form(opt) {\n const form = Formidable({\n maxFieldsSize: opt?.limit || defaults.form.limit,\n maxFileSize: opt?.limit || defaults.form.limit,\n ...opt,\n });\n return new Promise((resolve, reject) => {\n form.parse(ctx.request.req, (err, fields, files) => {\n if (err) {\n return reject(err);\n }\n resolve(\n unwrap(\n // files should go last to override fields in case of name conflicting\n { ...fields, ...files },\n opt?.unwrap,\n ),\n );\n });\n });\n },\n\n raw,\n };\n};\n";
667
+ var router_default$5 = "import { command } from \"virtual:kosmo/env\";\nimport type { RouterMiddleware } from \"@koa/router\";\n\nimport { createRoutes } from \"@kosmojs/core/api\";\n\nimport type { ParameterizedMiddleware } from \"../api\";\nimport { createBodyparsers, createMetaparsers } from \"./parsers\";\nimport { routeSources } from \"./routes\";\n\nimport globalMiddleware from \"{{ createImport 'api' 'use' }}\";\n\nexport const routes = createRoutes<ParameterizedMiddleware, RouterMiddleware>(\n routeSources,\n {\n productionBuild: command === \"build\",\n responseResolver(ctx) {\n return {\n status: ctx.status,\n contentType: ctx.type,\n async body() {\n return ctx.body;\n },\n };\n },\n createBodyparsers,\n createMetaparsers,\n globalMiddleware: globalMiddleware as never,\n },\n);\n";
668
+ var routes_default$1 = "import { posix } from \"node:path\";\n\nimport type { RouteSource } from \"@kosmojs/core/api\";\n\nimport {\n backendBase,\n apiRouteMap,\n apiRouteMapper,\n // needed for cascadingMiddleware\n type Override,\n} from \"{{ createImport 'libCore' }}\";\n\n{{#each routes}}\nimport {{id}} from \"{{ createImport 'api' file }}\";\nimport { validationSchemas as {{id}}_schemas } from \"{{ createImport 'libApi' basename 'schemas' }}\";\n{{/each}}\n\n{{#each cascadingMiddleware}}\nimport {{id}}, { type UseT as UseT{{id}} } from \"{{ createImport 'api' file }}\";\n{{/each}}\n\nexport type RouteMap = {\n{{#each routes}}\n \"{{name}}\": {\n paramsDefaults: {{paramsDefaults .}},\n paramsMappings: {{paramsMappings .}},\n cascadingState: {{cascadingState .}},\n },\n{{/each}}\n}\n\nexport const routeSources: Array<RouteSource<never>> = [\n {{#each routes}}\n {\n {{#if alias}}\n ...apiRouteMapper(\"/\", {\n ...{{serializeApiRoute .}},\n pathPattern: \"{{aliasPattern}}\",\n }),\n path: \"{{alias}}\",\n pathPattern: \"/{{aliasPattern}}\",\n {{else}}\n ...apiRouteMap[\"{{name}}\"],\n path: posix.join(backendBase, \"{{path}}\"),\n pathPattern: posix.join(backendBase, \"{{pathPattern}}\"),\n {{/if}}\n name: \"{{name}}\",\n file: \"{{file}}\",\n cascadingMiddleware: [ {{#each cascadingMiddleware}}{{id}}, {{/each}}].flat() as Array<never>,\n definitionItems: {{id}} as never,\n {{#if ../validationEnabled}}\n validationSchemas: {{id}}_schemas,\n {{/if}}\n },\n {{/each}}\n];\n";
767
669
  var server_default$1$8 = "import { chmod, unlink } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { parseArgs, styleText } from \"node:util\";\n\nimport type { App } from \"./app\";\n\nexport type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;\n\n/**\n * Wrap the app into a node:http request listener.\n * Used by dist/run.js to mount this folder's API next to other folders;\n * the standalone server (`serve`) calls app.listen() directly instead.\n * */\nexport const createListener = <T extends App>(app: T): NodeListener => {\n return app.callback();\n};\n\ntype Handles = {\n port?: number | undefined;\n sock?: string | undefined;\n onListen?: () => Promise<void>;\n};\n\nconst getListenHandles = async (opt?: Handles): Promise<Handles> => {\n const { port, sock } = opt\n ? opt\n : parseArgs({\n options: {\n port: {\n type: \"string\",\n short: \"p\",\n },\n sock: {\n type: \"string\",\n short: \"s\",\n },\n },\n }).values;\n\n if (![port, sock].some(Boolean)) {\n throw new Error(\"Please provide either -p/--port number or -s/--sock path\");\n }\n\n if (sock) {\n await unlink(sock).catch((error) => {\n if (error.code !== \"ENOENT\") {\n throw error;\n }\n });\n }\n\n const onListen = async () => {\n if (sock) {\n // Make Unix socket world-writable so other processes (e.g. a reverse proxy)\n // can connect without permission issues.\n await chmod(sock, 0o777);\n }\n console.log(\n `\\n ✨ Server Started ${styleText([\"dim\"], \"[ %s ]\")}`,\n sock ? `sock: ${sock}` : `port: ${port}`,\n );\n };\n\n return {\n port: port ? Number(port) : undefined,\n sock,\n onListen: opt?.onListen || onListen,\n };\n};\n\nexport const serve = async <T extends App>(app: T, opt?: Handles) => {\n const { port, sock, onListen } = await getListenHandles(opt);\n const server = app.listen(port || sock, onListen);\n return server as never;\n};\n";
768
- var api_default = "import type { RouterContext } from \"@koa/router\";\nimport type { Next } from \"koa\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { BodyparserOptions } from \"./@api/parsers\";\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultState {}\nexport interface DefaultContext {}\n\ntype ExtractBodies<R> = R extends [number, string, infer Body] ? Body : never;\n\ntype ValidatedResponseBodies<VDefs extends ValidationDefmap> = [\n ExtractBodies<VDefs[\"response\"]>,\n] extends [never]\n ? unknown // No bodies extracted at all - fallback to unknown\n : ExtractBodies<VDefs[\"response\"]>;\n\nexport type ParameterizedContext<\n ParamsT,\n StateT,\n ContextT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = RouterContext<\n DefaultState & StateT,\n DefaultContext &\n ContextT &\n ExtendContext<ParamsT, VDefs, VOpts, BodyparserOptions>,\n ValidatedResponseBodies<VDefs>\n>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n StateT = Record<string, unknown>,\n ContextT = Record<string, unknown>,\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT>,\n next: Next,\n) => Promise<void> | void;\n\nexport type RouteHandler<\n ParamsT,\n StateT,\n ContextT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT, VDefs, VOpts>,\n next: Next,\n) => Promise<void> | void;\n\nexport type DefineRouteFactory<ParamsT, StateT, ContextT> = (\n a: {\n // INFO: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, StateT, ContextT>\n | Array<ParameterizedMiddleware<ParamsT, StateT, ContextT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<\n ParameterizedMiddleware<ParamsT, StateT, ContextT>\n >;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, StateT, ContextT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, StateT, ContextT, VDefs, VOpts>>,\n ) => HandlerDefinition<ParameterizedMiddleware<ParamsT, StateT, ContextT>>;\n },\n) => Array<\n RouteDefinitionItem<ParameterizedMiddleware<ParamsT, StateT, ContextT>>\n>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const use = <StateT = DefaultState>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, StateT>\n | Array<ParameterizedMiddleware<Record<string, string>, StateT>>,\n options?: UseOptions,\n) => {\n return createUse<ParameterizedMiddleware<Record<string, string>, StateT>>(\n middleware,\n options,\n );\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n StateT extends object = object,\n ContextT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n StateT & RouteMap[R][\"cascadingState\"],\n ContextT\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n StateT & RouteMap[R][\"cascadingState\"],\n ContextT\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
670
+ var api_default = "import type { RouterContext } from \"@koa/router\";\nimport type { Next } from \"koa\";\n\nimport type { ValidationDefmap, ValidationOptmap } from \"@kosmojs/core\";\nimport {\n use as createUse,\n type DefaultUseSlots,\n type ExtendContext,\n type HandlerDefinition,\n type HTTPMethod,\n type MiddlewareDefinition,\n type RouteDefinitionItem,\n type UseOptions,\n} from \"@kosmojs/core/api\";\n\nimport type { BodyparserOptions } from \"./@api/parsers\";\nimport type { RouteMap } from \"./@api/routes\";\n\nexport interface DefaultState {}\nexport interface DefaultContext {}\nexport interface UseSlots extends DefaultUseSlots {}\n\ntype ExtractBodies<R> = R extends [number, string, infer Body] ? Body : never;\n\ntype ValidatedResponseBodies<VDefs extends ValidationDefmap> = [\n ExtractBodies<VDefs[\"response\"]>,\n] extends [never]\n ? unknown // No bodies extracted at all - fallback to unknown\n : ExtractBodies<VDefs[\"response\"]>;\n\nexport type ParameterizedContext<\n ParamsT,\n StateT,\n ContextT,\n VDefs extends ValidationDefmap = {},\n VOpts extends ValidationOptmap = {},\n> = RouterContext<\n DefaultState & StateT,\n DefaultContext &\n ContextT &\n ExtendContext<ParamsT, VDefs, VOpts, BodyparserOptions>,\n ValidatedResponseBodies<VDefs>\n>;\n\nexport type ParameterizedMiddleware<\n ParamsT = Record<string, string>,\n StateT = Record<string, unknown>,\n ContextT = Record<string, unknown>,\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT>,\n next: Next,\n) => Promise<void> | void;\n\nexport type RouteHandler<\n ParamsT,\n StateT,\n ContextT,\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n> = (\n ctx: ParameterizedContext<ParamsT, StateT, ContextT, VDefs, VOpts>,\n next: Next,\n) => Promise<void> | void;\n\nexport type DefineRouteFactory<ParamsT, StateT, ContextT> = (\n a: {\n // INFO: The `use` helper intentionally does not accept validation types.\n // Allowing these type parameters on `use` would be misleading,\n // since middleware operates across multiple request methods with varying types.\n use: (\n middleware:\n | ParameterizedMiddleware<ParamsT, StateT, ContextT>\n | Array<ParameterizedMiddleware<ParamsT, StateT, ContextT>>,\n options?: UseOptions,\n ) => MiddlewareDefinition<\n ParameterizedMiddleware<ParamsT, StateT, ContextT>\n >;\n } & {\n [M in HTTPMethod]: <\n VDefs extends ValidationDefmap,\n VOpts extends ValidationOptmap = {},\n >(\n handler:\n | RouteHandler<ParamsT, StateT, ContextT, VDefs, VOpts>\n | Array<RouteHandler<ParamsT, StateT, ContextT, VDefs, VOpts>>,\n ) => HandlerDefinition<ParameterizedMiddleware<ParamsT, StateT, ContextT>>;\n },\n) => Array<\n RouteDefinitionItem<ParameterizedMiddleware<ParamsT, StateT, ContextT>>\n>;\n\ntype ParamsMap<\n Mappings extends Array<[string, unknown, boolean]>,\n Refinements extends Array<unknown>,\n> = {\n [I in Extract<keyof Mappings, `${number}`> as Mappings[I] extends [\n infer ParamName extends string,\n ...Array<unknown>,\n ]\n ? ParamName\n : never]: Mappings[I] extends [string, infer Default, true]\n ? I extends keyof Refinements\n ? Refinements[I]\n : Default\n : Mappings[I] extends [string, infer Default, false]\n ? I extends keyof Refinements\n ? Refinements[I] | undefined\n : Default | undefined\n : never;\n};\n\nexport const use = <StateT = DefaultState>(\n middleware:\n | ParameterizedMiddleware<Record<string, string>, StateT>\n | Array<ParameterizedMiddleware<Record<string, string>, StateT>>,\n options?: UseOptions<keyof UseSlots>,\n) => {\n return createUse<\n ParameterizedMiddleware<Record<string, string>, StateT>,\n UseOptions<keyof UseSlots>\n >(middleware, options);\n};\n\nexport const defineRoute: <\n R extends keyof RouteMap,\n ParamsD extends RouteMap[R][\"paramsDefaults\"] = RouteMap[R][\"paramsDefaults\"],\n StateT extends object = object,\n ContextT extends object = object,\n>(\n factory: DefineRouteFactory<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n StateT & RouteMap[R][\"cascadingState\"],\n ContextT\n >,\n) => Array<\n RouteDefinitionItem<\n ParameterizedMiddleware<\n ParamsMap<RouteMap[R][\"paramsMappings\"], ParamsD>,\n StateT & RouteMap[R][\"cascadingState\"],\n ContextT\n >\n >\n> = (factory) => {\n const createHandler = <MiddlewareT>(method: HTTPMethod) => {\n return (middleware: MiddlewareT | Array<MiddlewareT>) => {\n return {\n kind: \"handler\",\n method,\n middleware: [middleware].flat(),\n };\n };\n };\n return factory({\n HEAD: createHandler(\"HEAD\") as never,\n OPTIONS: createHandler(\"OPTIONS\") as never,\n GET: createHandler(\"GET\") as never,\n POST: createHandler(\"POST\") as never,\n PUT: createHandler(\"PUT\") as never,\n PATCH: createHandler(\"PATCH\") as never,\n DELETE: createHandler(\"DELETE\") as never,\n // route-specific `use`, contains types for current route\n use: use as never,\n });\n};\n";
769
671
  var api_factory_default = "export * from \"./@api/app\";\nexport { appFactory as default } from \"./@api/app\";\nexport * from \"./@api/dev\";\nexport * from \"./@api/errors\";\nexport * from \"./@api/router\";\nexport * from \"./@api/routes\";\nexport * from \"./@api/server\";\n";
770
672
  var app_default$5 = "import appFactory, { routes } from \"{{ createImport 'lib' 'api:factory' }}\";\nimport defaultErrorHandler from \"./errors\";\n\nexport default appFactory(routes, ({ app }) => {\n app.use(defaultErrorHandler);\n})\n";
771
673
  var dev_default = "import app from \"./app\";\n\nimport { devSetup } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default devSetup({\n requestHandler() {\n return app.callback();\n },\n teardownHandler() {\n // close db connections, server sockets etc.\n },\n});\n\nprocess.on(\"unhandledRejection\", (reason) => {\n console.error(\"💥 UNHANDLED REJECTION\");\n console.error(\"Reason:\", reason);\n process.exit(1);\n});\n";
772
- var env_d_default$5 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultState {}\n interface DefaultContext {}\n}\n";
674
+ var env_d_default$5 = "export declare module \"{{ createImport 'libApi' }}\" {\n interface DefaultState {}\n interface DefaultContext {}\n interface UseSlots {}\n}\n";
773
675
  var errors_default = "import { HTTPError, ValidationError } from \"@kosmojs/core/errors\";\n\nimport { errorHandlerFactory } from \"{{ createImport 'lib' 'api:factory' }}\";\n\nexport default errorHandlerFactory(async (ctx, next) => {\n try {\n await next();\n } catch (error: any) {\n const [status, message] = Array.isArray(error)\n ? error\n : error instanceof HTTPError\n ? [error.status, error.message]\n : error instanceof ValidationError\n ? [400, `${error.target}: ${error.errorMessage}`]\n : [error.statusCode || 500, error.message];\n\n ctx.status = status;\n\n if (ctx.accepts(\"json\")) {\n ctx.body = { error: message };\n } else {\n ctx.body = message;\n }\n }\n});\n";
774
676
  var route_default = "import { defineRoute } from \"{{ createImport 'libApi' }}\";\n\nexport default defineRoute<\"{{route.name}}\">(({ GET }) => [\n GET(async (ctx) => {\n ctx.body = \"{{route.name}} route starts here - replace this response with real logic.\";\n }),\n]);\n";
775
677
  var use_default$1$1 = "import { use } from \"{{ createImport 'libApi' }}\";\n\nexport type UseT = {};\n\nexport default [\n use<UseT>(async (ctx, next) => {\n // Always `return next()` or `await next()`;\n // ❗ Never call `next()` by itself!\n return next();\n }),\n];\n";
@@ -839,11 +741,12 @@ var factory_default$9 = defineGeneratorFactory((sourceFolder) => {
839
741
  }).sort(sortRoutes);
840
742
  for (const [file, template] of [["@api/routes.ts", routes_default$1]]) await deployLibFile(createPath.lib(file), template, {
841
743
  routes: routesAndAliases,
842
- cascadingMiddleware
744
+ cascadingMiddleware,
745
+ validationEnabled: sourceFolder.config.validation ? true : false
843
746
  });
844
747
  };
845
748
  return {
846
- async start() {
749
+ async seed() {
847
750
  for (const [file, template] of [
848
751
  ["api.ts", api_default],
849
752
  ["api:factory.ts", api_factory_default],
@@ -6542,7 +6445,8 @@ var package_default$6 = {
6542
6445
  type: "module",
6543
6446
  "private": true,
6544
6447
  name: "@kosmojs/mdx-generator",
6545
- version: "0.3.0",
6448
+ version: "0.0.0",
6449
+ description: "MDX integration for KosmoJS (internal)",
6546
6450
  author: "Slee Woo",
6547
6451
  license: "MIT",
6548
6452
  files: ["pkg/*"],
@@ -6684,7 +6588,7 @@ var factory_default$8 = defineGeneratorFactory((sourceFolder) => {
6684
6588
  plugins: [plugin, ...plugins_default(sourceFolder, command)]
6685
6589
  };
6686
6590
  },
6687
- async start() {
6591
+ async seed() {
6688
6592
  for (const [file, template] of [
6689
6593
  ["env.d.ts", env_d_default$4],
6690
6594
  ["app.ts", app_default$1$6],
@@ -7148,6 +7052,7 @@ var factory_default$7 = defineGeneratorFactory((sourceFolder, options) => {
7148
7052
  await renderToFile(createPath.src(outfile), output, {});
7149
7053
  };
7150
7054
  return {
7055
+ async seed() {},
7151
7056
  async watch(entries) {
7152
7057
  await generateSchemas(entries);
7153
7058
  },
@@ -7169,7 +7074,8 @@ var package_default$5 = {
7169
7074
  type: "module",
7170
7075
  "private": true,
7171
7076
  name: "@kosmojs/react-generator",
7172
- version: "0.3.0",
7077
+ version: "0.0.0",
7078
+ description: "React integration for KosmoJS (internal)",
7173
7079
  author: "Slee Woo",
7174
7080
  license: "MIT",
7175
7081
  files: ["pkg/*"],
@@ -7367,7 +7273,7 @@ var factory_default$6 = defineGeneratorFactory((sourceFolder) => {
7367
7273
  const { plugin = defaultPlugin() } = typeof sourceFolder.config.frontend?.stack === "object" ? sourceFolder.config.frontend.stack : {};
7368
7274
  return { plugins: [plugin] };
7369
7275
  },
7370
- async start() {
7276
+ async seed() {
7371
7277
  for (const [file, template] of [
7372
7278
  ["env.d.ts", env_d_default$3],
7373
7279
  ["react.ts", react_default],
@@ -7429,7 +7335,8 @@ var package_default$4 = {
7429
7335
  type: "module",
7430
7336
  "private": true,
7431
7337
  name: "@kosmojs/solid-generator",
7432
- version: "0.3.0",
7338
+ version: "0.0.0",
7339
+ description: "SolidJS integration for KosmoJS (internal)",
7433
7340
  author: "Slee Woo",
7434
7341
  license: "MIT",
7435
7342
  files: ["pkg/*"],
@@ -7587,7 +7494,6 @@ var welcome_default$4 = "export { default } from \"{{ createImport 'lib' 'pageSa
7587
7494
  var router_default$4 = "import routerFactory, { createRouters } from \"{{ createImport 'lib' 'router' }}\";\n\nimport app from \"./app\";\n\nexport default routerFactory((routes) => {\n const { clientRouter, serverRouter } = createRouters(routes, { app });\n return {\n clientRouter() {\n return clientRouter()\n },\n serverRouter(url) {\n return serverRouter(url)\n },\n };\n});\n";
7588
7495
  var factory_default$5 = defineGeneratorFactory((sourceFolder) => {
7589
7496
  const { frontend } = sourceFolder.config;
7590
- const { generators = [] } = sourceFolder.config;
7591
7497
  const { createPath, createImportHelpers } = pathResolver(sourceFolder);
7592
7498
  const { render: renderLibTpl, renderToFile: deployLibFile } = renderFactory({
7593
7499
  helpers: {
@@ -7627,7 +7533,7 @@ var factory_default$5 = defineGeneratorFactory((sourceFolder) => {
7627
7533
  return {
7628
7534
  viteConfig() {
7629
7535
  const defaultPlugin = () => {
7630
- return vitePlugin$2({ ...generators.some((e) => e.meta.slot === "ssr") ? {
7536
+ return vitePlugin$2({ ...sourceFolder.generators.some((e) => e.meta.slot === "ssr") ? {
7631
7537
  ssr: true,
7632
7538
  solid: { hydratable: true }
7633
7539
  } : {} });
@@ -7638,7 +7544,7 @@ var factory_default$5 = defineGeneratorFactory((sourceFolder) => {
7638
7544
  plugins: [plugin]
7639
7545
  };
7640
7546
  },
7641
- async start() {
7547
+ async seed() {
7642
7548
  for (const [file, template] of [
7643
7549
  ["env.d.ts", env_d_default$2],
7644
7550
  ["solid.ts", solid_default],
@@ -7711,7 +7617,7 @@ var factory_default$4 = defineGeneratorFactory((sourceFolder) => {
7711
7617
  await deployLibFile(createPath.lib("ssg:routes.ts"), ssg_routes_default, { pageRoutes });
7712
7618
  };
7713
7619
  return {
7714
- async start() {
7620
+ async seed() {
7715
7621
  await deployLibFile(createPath.lib("ssg.ts"), ssg_default, {});
7716
7622
  },
7717
7623
  async watch(entries) {
@@ -7726,8 +7632,8 @@ var factory_default$4 = defineGeneratorFactory((sourceFolder) => {
7726
7632
  const ssrServerPath = resolve(dir, "../ssr/server.js");
7727
7633
  if (!await access(ssrServerPath, constants.F_OK).then(() => true, () => false)) {
7728
7634
  console.error();
7729
- console.error(styleText("red", `❗Please enable ssrGenerator in ${sourceFolder.name}/kosmo.config.ts`));
7730
- console.error(" SSG generator can not run without SSR server");
7635
+ console.error(styleText("red", `❗Please set frontend.ssr to true in ${sourceFolder.name}/kosmo.config.ts`));
7636
+ console.error(" SSG can not run without SSR server");
7731
7637
  console.error();
7732
7638
  return;
7733
7639
  }
@@ -7735,9 +7641,9 @@ var factory_default$4 = defineGeneratorFactory((sourceFolder) => {
7735
7641
  spinner.append("preparing...");
7736
7642
  const { createApp } = await import(ssrServerPath);
7737
7643
  spinner.append("bundling routes...");
7738
- await build(mergeConfigs(config.frontend.viteConfig, ...config.generators.map(({ factory }) => {
7644
+ await build(mergeConfigs(config.frontend.viteConfig, ...sourceFolder.generators.map(({ factory }) => {
7739
7645
  return factory(sourceFolder).viteConfig?.({
7740
- kind: "client",
7646
+ kind: "frontend",
7741
7647
  command: "build"
7742
7648
  });
7743
7649
  }), {
@@ -7783,7 +7689,7 @@ var factory_default$4 = defineGeneratorFactory((sourceFolder) => {
7783
7689
  return "error" in status ? [[route, status.error]] : [];
7784
7690
  });
7785
7691
  if (failed.length) {
7786
- spinner.failed("failed ❗");
7692
+ spinner.failed();
7787
7693
  throw new Error([`SSG: failed rendering ${failed.length} route(s):`, ...failed.map(([route, error]) => ` ${route} - ${error}`)].join("\n"));
7788
7694
  }
7789
7695
  await cp(resolve(dir, "../ssr/assets"), join(dir, "assets"), { recursive: true });
@@ -7819,7 +7725,8 @@ var package_default$3 = {
7819
7725
  type: "module",
7820
7726
  "private": true,
7821
7727
  name: "@kosmojs/ssr-generator",
7822
- version: "0.3.0",
7728
+ version: "0.0.0",
7729
+ description: "Server-side rendering support for KosmoJS source folders (internal)",
7823
7730
  author: "Slee Woo",
7824
7731
  license: "MIT",
7825
7732
  files: ["pkg/*"],
@@ -7846,9 +7753,10 @@ var ssr_default = "import { access, chmod, constants, readFile, unlink } from \"
7846
7753
  var DEFAULT_RENDER_MODE = ["string", "stream"][0];
7847
7754
  var factory_default$3 = defineGeneratorFactory((sourceFolder, options) => {
7848
7755
  const { createPath, createImportHelpers } = pathResolver(sourceFolder);
7849
- const { frontend, generators } = sourceFolder.config;
7756
+ const { frontend } = sourceFolder.config;
7850
7757
  const { renderToFile: deployLibFile } = renderFactory({ helpers: { ...createImportHelpers({ origin: "lib" }) } });
7851
7758
  return {
7759
+ async seed() {},
7852
7760
  async build(entries) {
7853
7761
  const renderModeResolver = options?.renderMode ? typeof options.renderMode === "string" ? () => options.renderMode : createRouteResolver(options?.renderMode, "string") : () => DEFAULT_RENDER_MODE;
7854
7762
  const context = {
@@ -7877,9 +7785,9 @@ var factory_default$3 = defineGeneratorFactory((sourceFolder, options) => {
7877
7785
  command: "build"
7878
7786
  })
7879
7787
  ];
7880
- await build(mergeConfigs(frontend.viteConfig, ...generators.map(({ factory }) => {
7788
+ await build(mergeConfigs(frontend.viteConfig, ...sourceFolder.generators.map(({ factory }) => {
7881
7789
  return factory(sourceFolder).viteConfig?.({
7882
- kind: "client",
7790
+ kind: "frontend",
7883
7791
  command: "build"
7884
7792
  });
7885
7793
  }), {
@@ -7952,7 +7860,8 @@ var package_default$2 = {
7952
7860
  type: "module",
7953
7861
  "private": true,
7954
7862
  name: "@kosmojs/svelte-generator",
7955
- version: "0.3.0",
7863
+ version: "0.0.0",
7864
+ description: "Svelte integration for KosmoJS (internal)",
7956
7865
  author: "Slee Woo",
7957
7866
  license: "MIT",
7958
7867
  files: ["pkg/*"],
@@ -8057,7 +7966,7 @@ var factory_default$2 = defineGeneratorFactory((sourceFolder) => {
8057
7966
  const { plugin = defaultPlugin() } = typeof sourceFolder.config.frontend?.stack === "object" ? sourceFolder.config.frontend.stack : {};
8058
7967
  return { plugins: [plugin] };
8059
7968
  },
8060
- async start() {
7969
+ async seed() {
8061
7970
  for (const [file, template] of [
8062
7971
  ["env.d.ts", env_d_default$1],
8063
7972
  ["svelte.ts", svelte_default],
@@ -8123,8 +8032,8 @@ var package_default$1 = {
8123
8032
  type: "module",
8124
8033
  "private": true,
8125
8034
  name: "@kosmojs/typebox-generator",
8126
- version: "0.3.0",
8127
- cacheVersion: "1",
8035
+ version: "0.0.0",
8036
+ description: "TypeBox validation schema generation for KosmoJS (internal)",
8128
8037
  author: "Slee Woo",
8129
8038
  license: "MIT",
8130
8039
  files: ["pkg/*"],
@@ -8212,7 +8121,7 @@ var factory_default$1 = defineGeneratorFactory((sourceFolder, options) => {
8212
8121
  }
8213
8122
  };
8214
8123
  return {
8215
- async start() {
8124
+ async seed() {
8216
8125
  for (const [file, template] of [
8217
8126
  ["custom-types.ts", custom_types_default],
8218
8127
  ["error-handler.ts", error_handler_default],
@@ -8250,7 +8159,8 @@ var package_default = {
8250
8159
  type: "module",
8251
8160
  "private": true,
8252
8161
  name: "@kosmojs/vue-generator",
8253
- version: "0.3.0",
8162
+ version: "0.0.0",
8163
+ description: "Vue integration for KosmoJS (internal)",
8254
8164
  author: "Slee Woo",
8255
8165
  license: "MIT",
8256
8166
  files: ["pkg/*"],
@@ -8441,7 +8351,7 @@ var factory_default = defineGeneratorFactory((sourceFolder) => {
8441
8351
  const { plugin = defaultPlugin() } = typeof sourceFolder.config.frontend?.stack === "object" ? sourceFolder.config.frontend.stack : {};
8442
8352
  return { plugins: [plugin] };
8443
8353
  },
8444
- async start() {
8354
+ async seed() {
8445
8355
  for (const [file, template] of [
8446
8356
  ["env.d.ts", env_d_default],
8447
8357
  ["unwrap.ts", unwrap_default],
@@ -8509,7 +8419,7 @@ var backendGenerators = {
8509
8419
  };
8510
8420
  var defineConfig = (origConfig) => {
8511
8421
  const config = { ...origConfig };
8512
- const { frontend, backend, validation } = config;
8422
+ const { frontend, backend, fetch, validation } = config;
8513
8423
  for (const key of ["frontend", "backend"]) if (config[key]) {
8514
8424
  const { base } = config[key];
8515
8425
  let error;
@@ -8534,9 +8444,9 @@ var defineConfig = (origConfig) => {
8534
8444
  }
8535
8445
  if (frontend) {
8536
8446
  const stack = typeof frontend.stack === "object" ? frontend.stack.name : frontend.stack;
8537
- if (frontend.fetch) {
8538
- if (frontend.fetch === true) generators.push(src_default$1());
8539
- else if (frontend.fetch.generator) generators.push(frontend.fetch.generator);
8447
+ if (fetch) {
8448
+ if (fetch === true) generators.push(src_default$1());
8449
+ else if (fetch.generator) generators.push(fetch.generator);
8540
8450
  }
8541
8451
  if (frontend.generator) generators.push(frontend.generator);
8542
8452
  else generators.push(frontendGenerators[stack](frontend));
@@ -8551,7 +8461,7 @@ var defineConfig = (origConfig) => {
8551
8461
  }
8552
8462
  }
8553
8463
  return {
8554
- ...config,
8464
+ config,
8555
8465
  generators
8556
8466
  };
8557
8467
  };