theokit 0.2.4 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-typed-client-US2ZXBEC.js → app-typed-client-62FYBLVJ.js} +3 -3
- package/dist/{app-typed-client-6GC6VWXS.js → app-typed-client-ITIYTBXO.js} +3 -3
- package/dist/{build-MREW6MVS.js → build-HIGHJQQV.js} +8 -8
- package/dist/{chunk-YWWHK7R6.js → chunk-2VQKDRVF.js} +2 -2
- package/dist/{chunk-2BQYEBCK.js → chunk-B6RP57M3.js} +84 -2
- package/dist/chunk-B6RP57M3.js.map +1 -0
- package/dist/{chunk-5OKZY2VL.js → chunk-BIGBFZSU.js} +10 -5
- package/dist/chunk-BIGBFZSU.js.map +1 -0
- package/dist/{chunk-X2OLD264.js → chunk-JAAHOGKI.js} +10 -5
- package/dist/chunk-JAAHOGKI.js.map +1 -0
- package/dist/{chunk-SNDZQ64K.js → chunk-OXIQI2XZ.js} +84 -2
- package/dist/chunk-OXIQI2XZ.js.map +1 -0
- package/dist/{chunk-BE2LKDI7.js → chunk-TRH2L3FI.js} +2 -2
- package/dist/cli/index.js +29 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{dev-6P2DM33Q.js → dev-266MVCVA.js} +6 -6
- package/dist/{dev-emit-PRSRPDHA.js → dev-emit-NO6OZJOQ.js} +3 -3
- package/dist/{dev-emit-MLDFOLUU.js → dev-emit-RBSFZZNO.js} +3 -3
- package/dist/{dist-E6BL4ZVY.js → dist-5V77Z223.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{openapi-SOKZTFE3.js → openapi-FNVSWZOL.js} +3 -3
- package/dist/{registry-5QFTLOL2.js → registry-4MZ26TZD.js} +1 -1
- package/dist/router-RGZFX75G.js +274 -0
- package/dist/router-RGZFX75G.js.map +1 -0
- package/dist/{routes-XVRAUH6H.js → routes-H4SPLFHJ.js} +2 -2
- package/dist/server/index.js +2 -2
- package/dist/server-routes-hmr-GZJGPWMS.js +44 -0
- package/dist/server-routes-hmr-GZJGPWMS.js.map +1 -0
- package/dist/server-routes-hmr-PBHSKCQJ.js +42 -0
- package/dist/server-routes-hmr-PBHSKCQJ.js.map +1 -0
- package/dist/{start-R7XSQL5L.js → start-HX3QUSOS.js} +5 -5
- package/dist/vite-plugin/index.js +2 -2
- package/dist/{vite-plugin-6MPHTKIW.js → vite-plugin-IK3NOWXS.js} +3 -3
- package/package.json +2 -2
- package/dist/chunk-2BQYEBCK.js.map +0 -1
- package/dist/chunk-5OKZY2VL.js.map +0 -1
- package/dist/chunk-SNDZQ64K.js.map +0 -1
- package/dist/chunk-X2OLD264.js.map +0 -1
- /package/dist/{app-typed-client-6GC6VWXS.js.map → app-typed-client-62FYBLVJ.js.map} +0 -0
- /package/dist/{app-typed-client-US2ZXBEC.js.map → app-typed-client-ITIYTBXO.js.map} +0 -0
- /package/dist/{build-MREW6MVS.js.map → build-HIGHJQQV.js.map} +0 -0
- /package/dist/{chunk-YWWHK7R6.js.map → chunk-2VQKDRVF.js.map} +0 -0
- /package/dist/{chunk-BE2LKDI7.js.map → chunk-TRH2L3FI.js.map} +0 -0
- /package/dist/{dev-6P2DM33Q.js.map → dev-266MVCVA.js.map} +0 -0
- /package/dist/{dev-emit-PRSRPDHA.js.map → dev-emit-NO6OZJOQ.js.map} +0 -0
- /package/dist/{dev-emit-MLDFOLUU.js.map → dev-emit-RBSFZZNO.js.map} +0 -0
- /package/dist/{dist-E6BL4ZVY.js.map → dist-5V77Z223.js.map} +0 -0
- /package/dist/{openapi-SOKZTFE3.js.map → openapi-FNVSWZOL.js.map} +0 -0
- /package/dist/{registry-5QFTLOL2.js.map → registry-4MZ26TZD.js.map} +0 -0
- /package/dist/{routes-XVRAUH6H.js.map → routes-H4SPLFHJ.js.map} +0 -0
- /package/dist/{start-R7XSQL5L.js.map → start-HX3QUSOS.js.map} +0 -0
- /package/dist/{vite-plugin-6MPHTKIW.js.map → vite-plugin-IK3NOWXS.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateManifest
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2VQKDRVF.js";
|
|
4
|
+
import "./chunk-B6RP57M3.js";
|
|
5
5
|
import "./chunk-7TOMTMHT.js";
|
|
6
6
|
import "./chunk-X2VVCJ4V.js";
|
|
7
7
|
import {
|
|
@@ -278,4 +278,4 @@ export {
|
|
|
278
278
|
emitClientDts,
|
|
279
279
|
generateClientDts
|
|
280
280
|
};
|
|
281
|
-
//# sourceMappingURL=app-typed-client-
|
|
281
|
+
//# sourceMappingURL=app-typed-client-62FYBLVJ.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import "tsx/esm";
|
|
3
3
|
import {
|
|
4
4
|
generateManifest
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TRH2L3FI.js";
|
|
6
6
|
import {
|
|
7
7
|
HTTP_METHODS
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OXIQI2XZ.js";
|
|
9
9
|
import "./chunk-5OFPQK6N.js";
|
|
10
10
|
import "./chunk-KT3MWNZG.js";
|
|
11
11
|
|
|
@@ -278,4 +278,4 @@ export {
|
|
|
278
278
|
emitClientDts,
|
|
279
279
|
generateClientDts
|
|
280
280
|
};
|
|
281
|
-
//# sourceMappingURL=app-typed-client-
|
|
281
|
+
//# sourceMappingURL=app-typed-client-ITIYTBXO.js.map
|
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
emitOpenApi,
|
|
5
5
|
loadRoutesForOpenApi
|
|
6
6
|
} from "./chunk-ZOXNJV2O.js";
|
|
7
|
-
import {
|
|
8
|
-
preflightNodeAndBindings
|
|
9
|
-
} from "./chunk-F3TG5FJV.js";
|
|
10
7
|
import {
|
|
11
8
|
validateProjectStructure
|
|
12
9
|
} from "./chunk-XMS5VRIK.js";
|
|
10
|
+
import {
|
|
11
|
+
preflightNodeAndBindings
|
|
12
|
+
} from "./chunk-F3TG5FJV.js";
|
|
13
13
|
import {
|
|
14
14
|
loadConfig,
|
|
15
15
|
loadEnv
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
generateManifest,
|
|
24
24
|
writeManifest as writeManifest2
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-TRH2L3FI.js";
|
|
26
|
+
import "./chunk-OXIQI2XZ.js";
|
|
27
27
|
import {
|
|
28
28
|
walkSourceFiles
|
|
29
29
|
} from "./chunk-5OFPQK6N.js";
|
|
@@ -520,7 +520,7 @@ async function buildCommand(options) {
|
|
|
520
520
|
`);
|
|
521
521
|
}
|
|
522
522
|
async function runAdapterBuild(target, config, cwd) {
|
|
523
|
-
const { theoPluginAsync } = await import("./vite-plugin-
|
|
523
|
+
const { theoPluginAsync } = await import("./vite-plugin-IK3NOWXS.js");
|
|
524
524
|
const { default: react } = await import("@vitejs/plugin-react");
|
|
525
525
|
const ctx = {
|
|
526
526
|
// `react()` may return Plugin or Plugin[] depending on version; spread the
|
|
@@ -528,7 +528,7 @@ async function runAdapterBuild(target, config, cwd) {
|
|
|
528
528
|
// type updated to `Plugin[] | Promise<Plugin[]>`).
|
|
529
529
|
makeVitePlugins: async (opts) => [react(), ...await theoPluginAsync(opts)].flat()
|
|
530
530
|
};
|
|
531
|
-
const { resolveAdapter } = await import("./registry-
|
|
531
|
+
const { resolveAdapter } = await import("./registry-4MZ26TZD.js");
|
|
532
532
|
const adapter = await resolveAdapter(target);
|
|
533
533
|
await adapter.build(config, cwd, ctx);
|
|
534
534
|
}
|
|
@@ -595,4 +595,4 @@ function relativize3(absPath, root) {
|
|
|
595
595
|
export {
|
|
596
596
|
buildCommand
|
|
597
597
|
};
|
|
598
|
-
//# sourceMappingURL=build-
|
|
598
|
+
//# sourceMappingURL=build-HIGHJQQV.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
compilePattern,
|
|
3
3
|
scanServerRoutes,
|
|
4
4
|
scanWebSocketRoutes
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-B6RP57M3.js";
|
|
6
6
|
import {
|
|
7
7
|
scanServerActions
|
|
8
8
|
} from "./chunk-7TOMTMHT.js";
|
|
@@ -70,4 +70,4 @@ export {
|
|
|
70
70
|
writeManifest,
|
|
71
71
|
loadManifest
|
|
72
72
|
};
|
|
73
|
-
//# sourceMappingURL=chunk-
|
|
73
|
+
//# sourceMappingURL=chunk-2VQKDRVF.js.map
|
|
@@ -34,7 +34,7 @@ function matchRoute(url, routes) {
|
|
|
34
34
|
|
|
35
35
|
// src/server/scan/scan.ts
|
|
36
36
|
import { existsSync, statSync } from "fs";
|
|
37
|
-
import { extname, join, relative } from "path";
|
|
37
|
+
import { basename, extname, join, relative } from "path";
|
|
38
38
|
|
|
39
39
|
// src/server/scan/detect-http-methods.ts
|
|
40
40
|
import { readFileSync } from "fs";
|
|
@@ -89,8 +89,88 @@ function detectExportedHttpMethods(filePath, content) {
|
|
|
89
89
|
return [...found].sort();
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// src/server/scan/errors.ts
|
|
93
|
+
var ROUTER_MIGRATION_GUIDE_URL = "https://theokit.dev/migration/0.3-to-0.4-router";
|
|
94
|
+
var RouterConventionError = class extends Error {
|
|
95
|
+
name = "RouterConventionError";
|
|
96
|
+
file;
|
|
97
|
+
suggestion;
|
|
98
|
+
migrationUrl;
|
|
99
|
+
constructor(opts) {
|
|
100
|
+
const migrationUrl = opts.migrationUrl ?? ROUTER_MIGRATION_GUIDE_URL;
|
|
101
|
+
const message = [
|
|
102
|
+
`Router convention violation: dotted route basename is not supported in theokit 0.4+.`,
|
|
103
|
+
``,
|
|
104
|
+
` File: ${opts.file}`,
|
|
105
|
+
` Use directory-nested form: ${opts.suggestion}`,
|
|
106
|
+
``,
|
|
107
|
+
`Migration guide: ${migrationUrl}`,
|
|
108
|
+
`Run \`theokit migrate router\` to convert all dotted basenames automatically.`
|
|
109
|
+
].join("\n");
|
|
110
|
+
super(message);
|
|
111
|
+
this.file = opts.file;
|
|
112
|
+
this.suggestion = opts.suggestion;
|
|
113
|
+
this.migrationUrl = migrationUrl;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
92
117
|
// src/server/scan/scan.ts
|
|
93
118
|
var ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
119
|
+
var TEST_OR_SPEC_RE = /\.(test|spec)\.[jt]sx?$/;
|
|
120
|
+
function isTestOrSpecFile(filePath) {
|
|
121
|
+
return TEST_OR_SPEC_RE.test(basename(filePath));
|
|
122
|
+
}
|
|
123
|
+
function hasDotOutsideBrackets(segment) {
|
|
124
|
+
let depth = 0;
|
|
125
|
+
for (const ch of segment) {
|
|
126
|
+
if (ch === "[") depth++;
|
|
127
|
+
else if (ch === "]") depth--;
|
|
128
|
+
else if (ch === "." && depth === 0) return true;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function splitDottedSegmentOutsideBrackets(segment) {
|
|
133
|
+
const parts = [];
|
|
134
|
+
let current = "";
|
|
135
|
+
let depth = 0;
|
|
136
|
+
for (const ch of segment) {
|
|
137
|
+
if (ch === "[") {
|
|
138
|
+
depth++;
|
|
139
|
+
current += ch;
|
|
140
|
+
} else if (ch === "]") {
|
|
141
|
+
depth--;
|
|
142
|
+
current += ch;
|
|
143
|
+
} else if (ch === "." && depth === 0) {
|
|
144
|
+
if (current) parts.push(current);
|
|
145
|
+
current = "";
|
|
146
|
+
} else {
|
|
147
|
+
current += ch;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (current) parts.push(current);
|
|
151
|
+
return parts;
|
|
152
|
+
}
|
|
153
|
+
function buildDirectoryNestedSuggestion(filePath, routesDir) {
|
|
154
|
+
const rel = relative(routesDir, filePath).replace(/\\/g, "/");
|
|
155
|
+
const ext = extname(rel);
|
|
156
|
+
const withoutExt = rel.slice(0, -ext.length);
|
|
157
|
+
const segments = withoutExt.split("/").flatMap(splitDottedSegmentOutsideBrackets);
|
|
158
|
+
return `routes/${segments.join("/")}${ext}`;
|
|
159
|
+
}
|
|
160
|
+
function assertNoDottedSegment(filePath, routesDir) {
|
|
161
|
+
const rel = relative(routesDir, filePath).replace(/\\/g, "/");
|
|
162
|
+
const ext = extname(rel);
|
|
163
|
+
const withoutExt = rel.slice(0, -ext.length);
|
|
164
|
+
const segments = withoutExt.split("/");
|
|
165
|
+
for (const seg of segments) {
|
|
166
|
+
if (hasDotOutsideBrackets(seg)) {
|
|
167
|
+
throw new RouterConventionError({
|
|
168
|
+
file: filePath,
|
|
169
|
+
suggestion: buildDirectoryNestedSuggestion(filePath, routesDir)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
94
174
|
function fileToRoutePath(filePath, routesDir) {
|
|
95
175
|
let rel = relative(routesDir, filePath);
|
|
96
176
|
const ext = extname(rel);
|
|
@@ -112,6 +192,8 @@ function scanServerRoutes(serverDir) {
|
|
|
112
192
|
}
|
|
113
193
|
const results = [];
|
|
114
194
|
walkSourceFiles(routesDir, { extensions: ROUTE_EXTENSIONS }, (absPath) => {
|
|
195
|
+
if (isTestOrSpecFile(absPath)) return;
|
|
196
|
+
assertNoDottedSegment(absPath, routesDir);
|
|
115
197
|
const routePath = fileToRoutePath(absPath, routesDir);
|
|
116
198
|
const { pattern, paramNames } = compilePattern(routePath);
|
|
117
199
|
const methods = detectExportedHttpMethods(absPath);
|
|
@@ -174,4 +256,4 @@ export {
|
|
|
174
256
|
scanServerRoutes,
|
|
175
257
|
scanWebSocketRoutes
|
|
176
258
|
};
|
|
177
|
-
//# sourceMappingURL=chunk-
|
|
259
|
+
//# sourceMappingURL=chunk-B6RP57M3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/scan/match.ts","../src/server/scan/scan.ts","../src/server/scan/detect-http-methods.ts","../src/server/scan/errors.ts","../src/server/scan/ws-scan.ts"],"sourcesContent":["export interface ServerRouteNode {\n filePath: string\n routePath: string\n paramNames: string[]\n pattern: RegExp\n /** HTTP methods (uppercase) the route file exports. Optional for backward\n * compatibility with manifests generated before G1. Empty array means the\n * file has no HTTP exports (util-only); undefined means \"not detected\". */\n methods?: string[]\n}\n\nexport function compilePattern(routePath: string): {\n pattern: RegExp\n paramNames: string[]\n} {\n const paramNames: string[] = []\n // Single pass: handle both catch-all (:...name) and regular (:name) params\n const regexStr = routePath.replace(/:(?:\\.\\.\\.)?([^/]+)/g, (match: string, name: string) => {\n paramNames.push(name)\n // Catch-all matches across slashes, regular matches single segment\n return match.startsWith(':...') ? '(.+)' : '([^/]+)'\n })\n // `regexStr` is derived from a developer-authored route path (build-time\n // input, not HTTP-controlled). The `security/detect-non-literal-regexp`\n // rule cannot see this constraint — disable narrowly.\n // eslint-disable-next-line security/detect-non-literal-regexp -- route pattern from build-time scan, never HTTP input\n return { pattern: new RegExp(`^${regexStr}$`), paramNames }\n}\n\nexport function matchRoute(\n url: string,\n routes: ServerRouteNode[],\n): { route: ServerRouteNode; params: Record<string, string> } | null {\n // Strip query string and trailing slash\n let path = url.split('?')[0]\n if (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n for (const route of routes) {\n const match = route.pattern.exec(path)\n if (match) {\n const params: Record<string, string> = {}\n route.paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { route, params }\n }\n }\n return null\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `serverDir/routes/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { existsSync, statSync } from 'node:fs'\nimport { basename, extname, join, relative } from 'node:path'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport { detectExportedHttpMethods } from './detect-http-methods.js'\nimport { RouterConventionError } from './errors.js'\nimport { compilePattern, type ServerRouteNode } from './match.js'\n\nconst ROUTE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\n// EC-4: Co-located unit/spec tests must be silently skipped by the scanner,\n// BEFORE the dotted-basename check fires. Matches `*.test.ts|tsx|js|jsx`\n// and `*.spec.ts|tsx|js|jsx`.\nconst TEST_OR_SPEC_RE = /\\.(test|spec)\\.[jt]sx?$/\n\nfunction isTestOrSpecFile(filePath: string): boolean {\n return TEST_OR_SPEC_RE.test(basename(filePath))\n}\n\nfunction hasDotOutsideBrackets(segment: string): boolean {\n let depth = 0\n for (const ch of segment) {\n if (ch === '[') depth++\n else if (ch === ']') depth--\n else if (ch === '.' && depth === 0) return true\n }\n return false\n}\n\nfunction splitDottedSegmentOutsideBrackets(segment: string): string[] {\n const parts: string[] = []\n let current = ''\n let depth = 0\n for (const ch of segment) {\n if (ch === '[') {\n depth++\n current += ch\n } else if (ch === ']') {\n depth--\n current += ch\n } else if (ch === '.' && depth === 0) {\n if (current) parts.push(current)\n current = ''\n } else {\n current += ch\n }\n }\n if (current) parts.push(current)\n return parts\n}\n\nfunction buildDirectoryNestedSuggestion(filePath: string, routesDir: string): string {\n const rel = relative(routesDir, filePath).replace(/\\\\/g, '/')\n const ext = extname(rel)\n const withoutExt = rel.slice(0, -ext.length)\n const segments = withoutExt.split('/').flatMap(splitDottedSegmentOutsideBrackets)\n return `routes/${segments.join('/')}${ext}`\n}\n\nfunction assertNoDottedSegment(filePath: string, routesDir: string): void {\n const rel = relative(routesDir, filePath).replace(/\\\\/g, '/')\n const ext = extname(rel)\n const withoutExt = rel.slice(0, -ext.length)\n const segments = withoutExt.split('/')\n for (const seg of segments) {\n if (hasDotOutsideBrackets(seg)) {\n throw new RouterConventionError({\n file: filePath,\n suggestion: buildDirectoryNestedSuggestion(filePath, routesDir),\n })\n }\n }\n}\n\nfunction fileToRoutePath(filePath: string, routesDir: string): string {\n let rel = relative(routesDir, filePath)\n // Strip extension\n const ext = extname(rel)\n rel = rel.slice(0, -ext.length)\n // Normalize separators\n rel = rel.replace(/\\\\/g, '/')\n // Strip index suffix\n if (rel.endsWith('/index')) {\n rel = rel.slice(0, -6)\n } else if (rel === 'index') {\n rel = ''\n }\n // Replace [...param] with :...param (catch-all, before regular params).\n // Replace [param] with :param. Inputs are file paths bounded by the\n // OS filename limit; the bracket capture is bounded by `]`.\n rel = rel.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, ':...$1')\n // eslint-disable-next-line sonarjs/slow-regex -- bounded by `]`; input is a single filename\n rel = rel.replace(/\\[([^\\]]+)\\]/g, ':$1')\n return `/api/${rel}`\n}\n\nexport function scanServerRoutes(serverDir: string): ServerRouteNode[] {\n const routesDir = join(serverDir, 'routes')\n if (!existsSync(routesDir) || !statSync(routesDir).isDirectory()) {\n return []\n }\n\n const results: ServerRouteNode[] = []\n walkSourceFiles(routesDir, { extensions: ROUTE_EXTENSIONS }, (absPath) => {\n // EC-4: skip co-located test/spec files BEFORE the dotted-basename check\n if (isTestOrSpecFile(absPath)) return\n\n // G6 T1.1: reject dotted basenames (legacy convention that produced wrong\n // paramNames due to greedy `:(?:\\.\\.\\.)?([^/]+)` regex in compilePattern).\n assertNoDottedSegment(absPath, routesDir)\n\n const routePath = fileToRoutePath(absPath, routesDir)\n const { pattern, paramNames } = compilePattern(routePath)\n const methods = detectExportedHttpMethods(absPath)\n results.push({\n filePath: absPath,\n routePath,\n paramNames,\n pattern,\n methods,\n })\n })\n\n // T1.4 / EC-2 — refuse to scan if a user route collides with the reserved\n // batch endpoint path. User must rename or disable batching.\n const conflicting = results.find((r) => r.routePath === '/api/__theo_batch__')\n if (conflicting) {\n throw new Error(\n `Server route ${conflicting.filePath} resolves to '/api/__theo_batch__' which is reserved for the batch endpoint. Rename the route or disable batching in theo.config.ts.`,\n )\n }\n\n // Sort: static first, then dynamic, then catch-all last\n const isCatchAll = (route: ServerRouteNode) => route.routePath.includes(':...')\n results.sort((a, b) => {\n const aStatic = a.paramNames.length === 0\n const bStatic = b.paramNames.length === 0\n const aCatchAll = isCatchAll(a)\n const bCatchAll = isCatchAll(b)\n\n // Static routes first\n if (aStatic && !bStatic) return -1\n if (!aStatic && bStatic) return 1\n // Catch-all routes last\n if (aCatchAll && !bCatchAll) return 1\n if (!aCatchAll && bCatchAll) return -1\n return a.routePath.localeCompare(b.routePath)\n })\n\n return results\n}\n","/**\n * Detect which HTTP-method named exports a route file declares.\n *\n * Uses the TypeScript compiler API (not regex) per G1 edge-case review EC-4:\n * regex over file content emits false positives for `// export const GET = ...`\n * in comments and `` `export const GET = ...` `` in template literals. AST\n * walking avoids both classes of bug.\n *\n * `typescript` ships as CommonJS with internal dynamic `require('fs')`.\n * When loaded via ESM `import`, the dynamic requires fail at module-bootstrap.\n * We use `createRequire(import.meta.url)` to keep the package on its native\n * CJS path. The type-only namespace import gives us the AST helpers shape.\n *\n * Returns the set of HTTP methods (uppercase) the file exports. Empty array\n * means the file has no HTTP exports (the route file is util-only).\n */\n\nimport { readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\n\nimport type * as TS from 'typescript'\n\nimport { HTTP_METHODS, type HttpMethod } from '../../core/contracts/http-methods.js'\n\nconst require_ = createRequire(import.meta.url)\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst ts = require_('typescript') as typeof TS\n\nconst HTTP_METHOD_NAMES = new Set<string>(HTTP_METHODS)\n\nfunction hasExportModifier(modifiers: readonly TS.Modifier[] | undefined): boolean {\n if (!modifiers) return false\n for (const m of modifiers) {\n if (m.kind === ts.SyntaxKind.ExportKeyword) return true\n }\n return false\n}\n\nfunction collectFromStatement(stmt: TS.Statement, found: Set<HttpMethod>): void {\n // `export const GET = ...` / `export function GET ...` / `export async function GET ...`\n if (ts.isVariableStatement(stmt) && hasExportModifier(ts.getModifiers(stmt))) {\n for (const decl of stmt.declarationList.declarations) {\n if (ts.isIdentifier(decl.name) && HTTP_METHOD_NAMES.has(decl.name.text)) {\n found.add(decl.name.text as HttpMethod)\n }\n }\n return\n }\n\n if (\n (ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) &&\n hasExportModifier(ts.getModifiers(stmt))\n ) {\n if (stmt.name && HTTP_METHOD_NAMES.has(stmt.name.text)) {\n found.add(stmt.name.text as HttpMethod)\n }\n return\n }\n\n // `export { GET }` / `export { handler as GET } from './shared'` (EC-5)\n if (ts.isExportDeclaration(stmt) && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const spec of stmt.exportClause.elements) {\n // spec.name is the exported (re-)name; spec.propertyName is the original (when renamed)\n if (HTTP_METHOD_NAMES.has(spec.name.text)) {\n found.add(spec.name.text as HttpMethod)\n }\n }\n }\n}\n\nexport function detectExportedHttpMethods(filePath: string, content?: string): HttpMethod[] {\n const src = content ?? readFileSync(filePath, 'utf-8')\n const sourceFile = ts.createSourceFile(\n filePath,\n src,\n ts.ScriptTarget.Latest,\n /* setParentNodes */ false,\n ts.ScriptKind.TS,\n )\n const found = new Set<HttpMethod>()\n for (const stmt of sourceFile.statements) {\n collectFromStatement(stmt, found)\n }\n return [...found].sort()\n}\n","/**\n * Router-convention errors thrown by the server-route scanner.\n *\n * Plan: .claude/knowledge-base/plans/g6-router-convention-plan.md v1.1\n *\n * theokit 0.4.0+ enforces directory-nested file-system routing\n * (`auth/[provider]/login.ts`) and REJECTS dotted-basename routes\n * (`auth.[provider].login.ts`) because the legacy regex extracted the\n * dotted basename incorrectly — `params.provider` was undefined at request\n * time. See ADR-XXX (router-convention-decision) in CHANGELOG 0.4.0.\n */\n\n/**\n * Canonical migration guide URL. T4.2 establishes this as the authoritative\n * landing page. EC-3: error message uses this constant so the URL never\n * drifts from the doc location.\n */\nexport const ROUTER_MIGRATION_GUIDE_URL = 'https://theokit.dev/migration/0.3-to-0.4-router'\n\nexport interface RouterConventionErrorOptions {\n /** Absolute path of the offending route file. */\n file: string\n /** Suggested directory-nested replacement path (relative, e.g. `routes/auth/[provider]/login.ts`). */\n suggestion: string\n /** Migration guide URL (defaults to `ROUTER_MIGRATION_GUIDE_URL`). */\n migrationUrl?: string\n}\n\n/**\n * Thrown by `scanServerRoutes` when a route file uses the legacy\n * dotted-basename convention (`auth.[provider].login.ts`).\n *\n * The error is FAIL-FAST by design — running with a route that has wrong\n * `paramNames` produces silent 404s at request time, which is strictly\n * worse than a build-time error.\n */\nexport class RouterConventionError extends Error {\n override readonly name = 'RouterConventionError'\n readonly file: string\n readonly suggestion: string\n readonly migrationUrl: string\n\n constructor(opts: RouterConventionErrorOptions) {\n const migrationUrl = opts.migrationUrl ?? ROUTER_MIGRATION_GUIDE_URL\n const message = [\n `Router convention violation: dotted route basename is not supported in theokit 0.4+.`,\n ``,\n ` File: ${opts.file}`,\n ` Use directory-nested form: ${opts.suggestion}`,\n ``,\n `Migration guide: ${migrationUrl}`,\n `Run \\`theokit migrate router\\` to convert all dotted basenames automatically.`,\n ].join('\\n')\n super(message)\n this.file = opts.file\n this.suggestion = opts.suggestion\n this.migrationUrl = migrationUrl\n }\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `serverDir/ws/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { existsSync, statSync } from 'node:fs'\nimport { extname, join, relative } from 'node:path'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nconst WS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\nexport interface WebSocketRouteNode {\n filePath: string\n wsPath: string\n}\n\nexport function scanWebSocketRoutes(serverDir: string): WebSocketRouteNode[] {\n const wsDir = join(serverDir, 'ws')\n if (!existsSync(wsDir) || !statSync(wsDir).isDirectory()) {\n return []\n }\n\n const results: WebSocketRouteNode[] = []\n walkSourceFiles(wsDir, { extensions: WS_EXTENSIONS }, (absPath) => {\n let rel = relative(wsDir, absPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -extname(rel).length)\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n else if (rel === 'index') rel = ''\n results.push({\n filePath: absPath,\n wsPath: `/ws/${rel}`,\n })\n })\n return results\n}\n"],"mappings":"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","names":["existsSync","statSync","extname","join","relative","join","existsSync","statSync","relative","extname"]}
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
matchRoute,
|
|
32
32
|
scanServerRoutes,
|
|
33
33
|
scanWebSocketRoutes
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-B6RP57M3.js";
|
|
35
35
|
import {
|
|
36
36
|
scanServerActions
|
|
37
37
|
} from "./chunk-7TOMTMHT.js";
|
|
@@ -1969,7 +1969,7 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
1969
1969
|
})
|
|
1970
1970
|
);
|
|
1971
1971
|
}
|
|
1972
|
-
const { appTypedClientPlugin } = await import("./app-typed-client-
|
|
1972
|
+
const { appTypedClientPlugin } = await import("./app-typed-client-62FYBLVJ.js");
|
|
1973
1973
|
const appClientPlugin = appTypedClientPlugin({
|
|
1974
1974
|
cwd: projectRoot,
|
|
1975
1975
|
serverDir: resolve6(projectRoot, "server"),
|
|
@@ -1980,12 +1980,17 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
1980
1980
|
serverDir: resolve6(projectRoot, "server"),
|
|
1981
1981
|
distDir: resolve6(projectRoot, ".theo")
|
|
1982
1982
|
});
|
|
1983
|
+
const { serverRoutesHmrPlugin } = await import("./server-routes-hmr-PBHSKCQJ.js");
|
|
1984
|
+
const routesHmrPlugin = serverRoutesHmrPlugin({
|
|
1985
|
+
serverDir: resolve6(projectRoot, "server")
|
|
1986
|
+
});
|
|
1983
1987
|
return [
|
|
1984
1988
|
theoPlugin(rootOrOptions),
|
|
1985
1989
|
...uiPlugins,
|
|
1986
1990
|
...servicesPlugins,
|
|
1987
1991
|
appClientPlugin,
|
|
1988
|
-
actionsPlugin
|
|
1992
|
+
actionsPlugin,
|
|
1993
|
+
routesHmrPlugin
|
|
1989
1994
|
];
|
|
1990
1995
|
}
|
|
1991
1996
|
function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
|
|
@@ -2238,7 +2243,7 @@ function theoPlugin(rootOrOptions) {
|
|
|
2238
2243
|
server.watcher.on("unlink", handleRouteChange);
|
|
2239
2244
|
if (resolvedOpenApi !== void 0) {
|
|
2240
2245
|
const openApiCfg = resolvedOpenApi;
|
|
2241
|
-
const { reEmitOpenApi } = await import("./dev-emit-
|
|
2246
|
+
const { reEmitOpenApi } = await import("./dev-emit-RBSFZZNO.js");
|
|
2242
2247
|
const openApiServerDir = resolve6(projectRoot, "server");
|
|
2243
2248
|
const openApiDistDir = resolve6(projectRoot, resolvedDistDir);
|
|
2244
2249
|
const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
|
|
@@ -2284,4 +2289,4 @@ export {
|
|
|
2284
2289
|
theoPluginAsync,
|
|
2285
2290
|
theoPlugin
|
|
2286
2291
|
};
|
|
2287
|
-
//# sourceMappingURL=chunk-
|
|
2292
|
+
//# sourceMappingURL=chunk-BIGBFZSU.js.map
|