theokit 0.1.0-alpha.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/build-NSKFOAFX.js +49 -0
- package/dist/build-NSKFOAFX.js.map +1 -0
- package/dist/chunk-ASGEGAWL.js +480 -0
- package/dist/chunk-ASGEGAWL.js.map +1 -0
- package/dist/chunk-ATSTRYYT.js +894 -0
- package/dist/chunk-ATSTRYYT.js.map +1 -0
- package/dist/chunk-KPU44T6G.js +71 -0
- package/dist/chunk-KPU44T6G.js.map +1 -0
- package/dist/chunk-MMZZBPMX.js +335 -0
- package/dist/chunk-MMZZBPMX.js.map +1 -0
- package/dist/chunk-N5YH2UDG.js +85 -0
- package/dist/chunk-N5YH2UDG.js.map +1 -0
- package/dist/chunk-SAVVU5LG.js +66 -0
- package/dist/chunk-SAVVU5LG.js.map +1 -0
- package/dist/chunk-TXMUCDJT.js +43 -0
- package/dist/chunk-TXMUCDJT.js.map +1 -0
- package/dist/chunk-U3OJFWK3.js +162 -0
- package/dist/chunk-U3OJFWK3.js.map +1 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +37 -0
- package/dist/client/index.js +57 -0
- package/dist/client/index.js.map +1 -0
- package/dist/cloudflare-CVGN7FOU.js +88 -0
- package/dist/cloudflare-CVGN7FOU.js.map +1 -0
- package/dist/dev-7UJK3M2O.js +47 -0
- package/dist/dev-7UJK3M2O.js.map +1 -0
- package/dist/docker-M253W54T.js +101 -0
- package/dist/docker-M253W54T.js.map +1 -0
- package/dist/generate-AA7ZE42F.js +116 -0
- package/dist/generate-AA7ZE42F.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limit-C6hHXIj1.d.ts +13 -0
- package/dist/routes-YP357VAC.js +60 -0
- package/dist/routes-YP357VAC.js.map +1 -0
- package/dist/server/index.d.ts +94 -0
- package/dist/server/index.js +148 -0
- package/dist/server/index.js.map +1 -0
- package/dist/start-BIS3RCFQ.js +243 -0
- package/dist/start-BIS3RCFQ.js.map +1 -0
- package/dist/vercel-747SR2FB.js +80 -0
- package/dist/vercel-747SR2FB.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +12 -0
- package/dist/vite-plugin/index.js +8 -0
- package/dist/vite-plugin/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
nodeAdapter
|
|
4
|
+
} from "./chunk-TXMUCDJT.js";
|
|
5
|
+
import "./chunk-ASGEGAWL.js";
|
|
6
|
+
import "./chunk-MMZZBPMX.js";
|
|
7
|
+
import "./chunk-U3OJFWK3.js";
|
|
8
|
+
|
|
9
|
+
// src/adapters/cloudflare.ts
|
|
10
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
var cloudflareAdapter = {
|
|
13
|
+
name: "cloudflare",
|
|
14
|
+
async build(config, cwd) {
|
|
15
|
+
await nodeAdapter.build(config, cwd);
|
|
16
|
+
const outputDir = resolve(cwd, ".theo/cloudflare");
|
|
17
|
+
mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
const workerEntry = [
|
|
19
|
+
`// Generated by Theo \u2014 Cloudflare Workers Adapter`,
|
|
20
|
+
`// Requires nodejs_compat and compatibility_date >= 2025-09-01`,
|
|
21
|
+
`// NOTE: WebSocket requires Cloudflare native WS, not 'ws' package`,
|
|
22
|
+
`// NOTE: In-memory rate limiting does not persist between invocations`,
|
|
23
|
+
`import { createServer } from 'node:http';`,
|
|
24
|
+
``,
|
|
25
|
+
`export default {`,
|
|
26
|
+
` async fetch(request, env, ctx) {`,
|
|
27
|
+
` return new Promise((resolve) => {`,
|
|
28
|
+
` const url = new URL(request.url);`,
|
|
29
|
+
` const headers = {};`,
|
|
30
|
+
` request.headers.forEach((v, k) => { headers[k] = v; });`,
|
|
31
|
+
``,
|
|
32
|
+
` const req = {`,
|
|
33
|
+
` method: request.method,`,
|
|
34
|
+
` url: url.pathname + url.search,`,
|
|
35
|
+
` headers,`,
|
|
36
|
+
` socket: { remoteAddress: headers['cf-connecting-ip'] || '0.0.0.0' },`,
|
|
37
|
+
` on: (event, cb) => {`,
|
|
38
|
+
` if (event === 'data') request.body?.pipeTo(new WritableStream({ write(chunk) { cb(chunk); } }));`,
|
|
39
|
+
` if (event === 'end') request.body ? request.body.pipeTo(new WritableStream({ close() { cb(); } })) : cb();`,
|
|
40
|
+
` },`,
|
|
41
|
+
` };`,
|
|
42
|
+
``,
|
|
43
|
+
` const chunks = [];`,
|
|
44
|
+
` const res = {`,
|
|
45
|
+
` statusCode: 200,`,
|
|
46
|
+
` _headers: {},`,
|
|
47
|
+
` writeHead(status, hdrs) { this.statusCode = status; Object.assign(this._headers, hdrs); },`,
|
|
48
|
+
` setHeader(k, v) { this._headers[k] = v; },`,
|
|
49
|
+
` getHeader(k) { return this._headers[k]; },`,
|
|
50
|
+
` write(chunk) { chunks.push(chunk); return true; },`,
|
|
51
|
+
` end(body) {`,
|
|
52
|
+
` if (body) chunks.push(typeof body === 'string' ? new TextEncoder().encode(body) : body);`,
|
|
53
|
+
` resolve(new Response(new Blob(chunks), { status: this.statusCode, headers: this._headers }));`,
|
|
54
|
+
` },`,
|
|
55
|
+
` headersSent: false,`,
|
|
56
|
+
` writableEnded: false,`,
|
|
57
|
+
` };`,
|
|
58
|
+
``,
|
|
59
|
+
` // Import and run Theo handler`,
|
|
60
|
+
` import('./handler.js').then(mod => mod.default(req, res)).catch(() => {`,
|
|
61
|
+
` resolve(new Response('Internal Server Error', { status: 500 }));`,
|
|
62
|
+
` });`,
|
|
63
|
+
` });`,
|
|
64
|
+
` },`,
|
|
65
|
+
`};`
|
|
66
|
+
].join("\n");
|
|
67
|
+
writeFileSync(resolve(outputDir, "worker.mjs"), workerEntry);
|
|
68
|
+
const wranglerToml = [
|
|
69
|
+
`# Generated by Theo \u2014 Cloudflare Workers`,
|
|
70
|
+
`name = "theo-app"`,
|
|
71
|
+
`main = ".theo/cloudflare/worker.mjs"`,
|
|
72
|
+
`compatibility_date = "2025-09-01"`,
|
|
73
|
+
`compatibility_flags = ["nodejs_compat"]`,
|
|
74
|
+
``,
|
|
75
|
+
`[site]`,
|
|
76
|
+
`bucket = ".theo/client"`,
|
|
77
|
+
``,
|
|
78
|
+
`# Environment variables are set via wrangler secret or dashboard`,
|
|
79
|
+
`# Example: wrangler secret put DATABASE_URL`
|
|
80
|
+
].join("\n");
|
|
81
|
+
writeFileSync(resolve(cwd, "wrangler.toml"), wranglerToml);
|
|
82
|
+
console.log("\n \u2713 Cloudflare output \u2192 .theo/cloudflare/ + wrangler.toml\n");
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export {
|
|
86
|
+
cloudflareAdapter
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=cloudflare-CVGN7FOU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/cloudflare.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { DeployAdapter } from './types.js'\nimport type { TheoConfig } from '../config/schema.js'\nimport { nodeAdapter } from './node.js'\n\nexport const cloudflareAdapter: DeployAdapter = {\n name: 'cloudflare',\n\n async build(config: TheoConfig, cwd: string): Promise<void> {\n // 1. Run the standard Node build first\n await nodeAdapter.build(config, cwd)\n\n const outputDir = resolve(cwd, '.theo/cloudflare')\n mkdirSync(outputDir, { recursive: true })\n\n // 2. Generate Worker entry\n const workerEntry = [\n `// Generated by Theo — Cloudflare Workers Adapter`,\n `// Requires nodejs_compat and compatibility_date >= 2025-09-01`,\n `// NOTE: WebSocket requires Cloudflare native WS, not 'ws' package`,\n `// NOTE: In-memory rate limiting does not persist between invocations`,\n `import { createServer } from 'node:http';`,\n ``,\n `export default {`,\n ` async fetch(request, env, ctx) {`,\n ` return new Promise((resolve) => {`,\n ` const url = new URL(request.url);`,\n ` const headers = {};`,\n ` request.headers.forEach((v, k) => { headers[k] = v; });`,\n ``,\n ` const req = {`,\n ` method: request.method,`,\n ` url: url.pathname + url.search,`,\n ` headers,`,\n ` socket: { remoteAddress: headers['cf-connecting-ip'] || '0.0.0.0' },`,\n ` on: (event, cb) => {`,\n ` if (event === 'data') request.body?.pipeTo(new WritableStream({ write(chunk) { cb(chunk); } }));`,\n ` if (event === 'end') request.body ? request.body.pipeTo(new WritableStream({ close() { cb(); } })) : cb();`,\n ` },`,\n ` };`,\n ``,\n ` const chunks = [];`,\n ` const res = {`,\n ` statusCode: 200,`,\n ` _headers: {},`,\n ` writeHead(status, hdrs) { this.statusCode = status; Object.assign(this._headers, hdrs); },`,\n ` setHeader(k, v) { this._headers[k] = v; },`,\n ` getHeader(k) { return this._headers[k]; },`,\n ` write(chunk) { chunks.push(chunk); return true; },`,\n ` end(body) {`,\n ` if (body) chunks.push(typeof body === 'string' ? new TextEncoder().encode(body) : body);`,\n ` resolve(new Response(new Blob(chunks), { status: this.statusCode, headers: this._headers }));`,\n ` },`,\n ` headersSent: false,`,\n ` writableEnded: false,`,\n ` };`,\n ``,\n ` // Import and run Theo handler`,\n ` import('./handler.js').then(mod => mod.default(req, res)).catch(() => {`,\n ` resolve(new Response('Internal Server Error', { status: 500 }));`,\n ` });`,\n ` });`,\n ` },`,\n `};`,\n ].join('\\n')\n\n writeFileSync(resolve(outputDir, 'worker.mjs'), workerEntry)\n\n // 3. Generate wrangler.toml\n const wranglerToml = [\n `# Generated by Theo — Cloudflare Workers`,\n `name = \"theo-app\"`,\n `main = \".theo/cloudflare/worker.mjs\"`,\n `compatibility_date = \"2025-09-01\"`,\n `compatibility_flags = [\"nodejs_compat\"]`,\n ``,\n `[site]`,\n `bucket = \".theo/client\"`,\n ``,\n `# Environment variables are set via wrangler secret or dashboard`,\n `# Example: wrangler secret put DATABASE_URL`,\n ].join('\\n')\n\n writeFileSync(resolve(cwd, 'wrangler.toml'), wranglerToml)\n\n console.log('\\n ✓ Cloudflare output → .theo/cloudflare/ + wrangler.toml\\n')\n },\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,WAAW,qBAAqB;AACzC,SAAS,eAAe;AAKjB,IAAM,oBAAmC;AAAA,EAC9C,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAA4B;AAE1D,UAAM,YAAY,MAAM,QAAQ,GAAG;AAEnC,UAAM,YAAY,QAAQ,KAAK,kBAAkB;AACjD,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAGxC,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAEX,kBAAc,QAAQ,WAAW,YAAY,GAAG,WAAW;AAG3D,UAAM,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAEX,kBAAc,QAAQ,KAAK,eAAe,GAAG,YAAY;AAEzD,YAAQ,IAAI,yEAA+D;AAAA,EAC7E;AACF;","names":[]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
theoPlugin
|
|
4
|
+
} from "./chunk-ASGEGAWL.js";
|
|
5
|
+
import "./chunk-MMZZBPMX.js";
|
|
6
|
+
import {
|
|
7
|
+
validateProjectStructure
|
|
8
|
+
} from "./chunk-KPU44T6G.js";
|
|
9
|
+
import {
|
|
10
|
+
loadConfig
|
|
11
|
+
} from "./chunk-N5YH2UDG.js";
|
|
12
|
+
import "./chunk-U3OJFWK3.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/commands/dev.ts
|
|
15
|
+
import { createServer } from "vite";
|
|
16
|
+
import react from "@vitejs/plugin-react";
|
|
17
|
+
async function startDevServer(cwd, options) {
|
|
18
|
+
const config = await loadConfig(cwd);
|
|
19
|
+
validateProjectStructure(cwd);
|
|
20
|
+
const port = options?.port ?? config.port;
|
|
21
|
+
const server = await createServer({
|
|
22
|
+
root: cwd,
|
|
23
|
+
plugins: [react(), theoPlugin({ root: cwd, rateLimit: config.rateLimit, ssr: config.ssr })],
|
|
24
|
+
server: { port },
|
|
25
|
+
logLevel: options?.port === 0 ? "silent" : void 0
|
|
26
|
+
});
|
|
27
|
+
await server.listen();
|
|
28
|
+
return server;
|
|
29
|
+
}
|
|
30
|
+
async function devCommand(options) {
|
|
31
|
+
try {
|
|
32
|
+
const cwd = process.cwd();
|
|
33
|
+
const server = await startDevServer(cwd, options);
|
|
34
|
+
server.printUrls();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
37
|
+
console.error(`
|
|
38
|
+
\u2717 ${msg}
|
|
39
|
+
`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
devCommand,
|
|
45
|
+
startDevServer
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=dev-7UJK3M2O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/dev.ts"],"sourcesContent":["import { createServer, type ViteDevServer } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { loadConfig } from '../../config/load-config.js'\nimport { validateProjectStructure } from '../../core/validate-structure.js'\nimport { theoPlugin } from '../../vite-plugin/index.js'\n\ninterface DevOptions {\n port?: number\n}\n\nexport async function startDevServer(\n cwd: string,\n options?: DevOptions,\n): Promise<ViteDevServer> {\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n const port = options?.port ?? config.port\n const server = await createServer({\n root: cwd,\n plugins: [react(), theoPlugin({ root: cwd, rateLimit: config.rateLimit, ssr: config.ssr })],\n server: { port },\n logLevel: options?.port === 0 ? 'silent' : undefined,\n })\n\n await server.listen()\n return server\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n try {\n const cwd = process.cwd()\n const server = await startDevServer(cwd, options)\n server.printUrls()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`\\n ✗ ${msg}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,oBAAwC;AACjD,OAAO,WAAW;AASlB,eAAsB,eACpB,KACA,SACwB;AACxB,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,2BAAyB,GAAG;AAE5B,QAAM,OAAO,SAAS,QAAQ,OAAO;AACrC,QAAM,SAAS,MAAM,aAAa;AAAA,IAChC,MAAM;AAAA,IACN,SAAS,CAAC,MAAM,GAAG,WAAW,EAAE,MAAM,KAAK,WAAW,OAAO,WAAW,KAAK,OAAO,IAAI,CAAC,CAAC;AAAA,IAC1F,QAAQ,EAAE,KAAK;AAAA,IACf,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,EAC7C,CAAC;AAED,QAAM,OAAO,OAAO;AACpB,SAAO;AACT;AAEA,eAAsB,WAAW,SAAoC;AACnE,MAAI;AACF,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,SAAS,MAAM,eAAe,KAAK,OAAO;AAChD,WAAO,UAAU;AAAA,EACnB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM;AAAA,WAAS,GAAG;AAAA,CAAI;AAC9B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/commands/docker.ts
|
|
4
|
+
import { existsSync, writeFileSync } from "fs";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
function detectPkgManager(cwd) {
|
|
7
|
+
if (existsSync(resolve(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8
|
+
if (existsSync(resolve(cwd, "yarn.lock"))) return "yarn";
|
|
9
|
+
return "npm";
|
|
10
|
+
}
|
|
11
|
+
function generateDockerfile(pkgManager) {
|
|
12
|
+
const installCmd = {
|
|
13
|
+
pnpm: "RUN corepack enable && pnpm install --frozen-lockfile",
|
|
14
|
+
npm: "RUN npm ci",
|
|
15
|
+
yarn: "RUN yarn install --frozen-lockfile"
|
|
16
|
+
}[pkgManager];
|
|
17
|
+
const lockfile = {
|
|
18
|
+
pnpm: "pnpm-lock.yaml",
|
|
19
|
+
npm: "package-lock.json",
|
|
20
|
+
yarn: "yarn.lock"
|
|
21
|
+
}[pkgManager];
|
|
22
|
+
const runBuild = {
|
|
23
|
+
pnpm: "RUN pnpm theo build",
|
|
24
|
+
npm: "RUN npx theo build",
|
|
25
|
+
yarn: "RUN yarn theo build"
|
|
26
|
+
}[pkgManager];
|
|
27
|
+
const startCmd = {
|
|
28
|
+
pnpm: 'CMD ["pnpm", "theo", "start"]',
|
|
29
|
+
npm: 'CMD ["npx", "theo", "start"]',
|
|
30
|
+
yarn: 'CMD ["yarn", "theo", "start"]'
|
|
31
|
+
}[pkgManager];
|
|
32
|
+
return [
|
|
33
|
+
"# Generated by Theo",
|
|
34
|
+
"# Multi-stage build for production",
|
|
35
|
+
"",
|
|
36
|
+
"# Stage 1: Build",
|
|
37
|
+
"FROM node:22-alpine AS builder",
|
|
38
|
+
"WORKDIR /app",
|
|
39
|
+
`COPY package.json ${lockfile} ./`,
|
|
40
|
+
installCmd,
|
|
41
|
+
"COPY . .",
|
|
42
|
+
runBuild,
|
|
43
|
+
"",
|
|
44
|
+
"# Stage 2: Production",
|
|
45
|
+
"FROM node:22-alpine AS runner",
|
|
46
|
+
"WORKDIR /app",
|
|
47
|
+
"ENV NODE_ENV=production",
|
|
48
|
+
"COPY --from=builder /app/.theo ./.theo",
|
|
49
|
+
"COPY --from=builder /app/server ./server",
|
|
50
|
+
"COPY --from=builder /app/theo.config.ts ./theo.config.ts",
|
|
51
|
+
"COPY --from=builder /app/package.json ./package.json",
|
|
52
|
+
`COPY --from=builder /app/${lockfile} ./${lockfile}`,
|
|
53
|
+
"COPY --from=builder /app/node_modules ./node_modules",
|
|
54
|
+
"",
|
|
55
|
+
"EXPOSE 3000",
|
|
56
|
+
startCmd,
|
|
57
|
+
""
|
|
58
|
+
].join("\n");
|
|
59
|
+
}
|
|
60
|
+
function generateDockerignore() {
|
|
61
|
+
return [
|
|
62
|
+
"node_modules",
|
|
63
|
+
".git",
|
|
64
|
+
".theo",
|
|
65
|
+
"dist",
|
|
66
|
+
".env",
|
|
67
|
+
".env.*",
|
|
68
|
+
"!.env.example",
|
|
69
|
+
"*.log",
|
|
70
|
+
".DS_Store",
|
|
71
|
+
".vscode",
|
|
72
|
+
".idea",
|
|
73
|
+
"Dockerfile",
|
|
74
|
+
".dockerignore",
|
|
75
|
+
""
|
|
76
|
+
].join("\n");
|
|
77
|
+
}
|
|
78
|
+
async function dockerCommand(options) {
|
|
79
|
+
const cwd = process.cwd();
|
|
80
|
+
const dockerfilePath = resolve(cwd, "Dockerfile");
|
|
81
|
+
const dockerignorePath = resolve(cwd, ".dockerignore");
|
|
82
|
+
if (existsSync(dockerfilePath) && !options?.force) {
|
|
83
|
+
console.log("\n \u26A0 Dockerfile already exists. Use --force to overwrite.\n");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const pkgManager = detectPkgManager(cwd);
|
|
87
|
+
writeFileSync(dockerfilePath, generateDockerfile(pkgManager));
|
|
88
|
+
writeFileSync(dockerignorePath, generateDockerignore());
|
|
89
|
+
console.log(`
|
|
90
|
+
\u2713 Generated Dockerfile (${pkgManager})`);
|
|
91
|
+
console.log(` \u2713 Generated .dockerignore`);
|
|
92
|
+
console.log(`
|
|
93
|
+
Next steps:`);
|
|
94
|
+
console.log(` docker build -t my-app .`);
|
|
95
|
+
console.log(` docker run -p 3000:3000 my-app
|
|
96
|
+
`);
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
dockerCommand
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=docker-M253W54T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/docker.ts"],"sourcesContent":["import { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nfunction detectPkgManager(cwd: string): 'pnpm' | 'npm' | 'yarn' {\n if (existsSync(resolve(cwd, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(resolve(cwd, 'yarn.lock'))) return 'yarn'\n return 'npm'\n}\n\nfunction generateDockerfile(pkgManager: 'pnpm' | 'npm' | 'yarn'): string {\n const installCmd = {\n pnpm: 'RUN corepack enable && pnpm install --frozen-lockfile',\n npm: 'RUN npm ci',\n yarn: 'RUN yarn install --frozen-lockfile',\n }[pkgManager]\n\n const lockfile = {\n pnpm: 'pnpm-lock.yaml',\n npm: 'package-lock.json',\n yarn: 'yarn.lock',\n }[pkgManager]\n\n const runBuild = {\n pnpm: 'RUN pnpm theo build',\n npm: 'RUN npx theo build',\n yarn: 'RUN yarn theo build',\n }[pkgManager]\n\n const startCmd = {\n pnpm: 'CMD [\"pnpm\", \"theo\", \"start\"]',\n npm: 'CMD [\"npx\", \"theo\", \"start\"]',\n yarn: 'CMD [\"yarn\", \"theo\", \"start\"]',\n }[pkgManager]\n\n return [\n '# Generated by Theo',\n '# Multi-stage build for production',\n '',\n '# Stage 1: Build',\n 'FROM node:22-alpine AS builder',\n 'WORKDIR /app',\n `COPY package.json ${lockfile} ./`,\n installCmd,\n 'COPY . .',\n runBuild,\n '',\n '# Stage 2: Production',\n 'FROM node:22-alpine AS runner',\n 'WORKDIR /app',\n 'ENV NODE_ENV=production',\n 'COPY --from=builder /app/.theo ./.theo',\n 'COPY --from=builder /app/server ./server',\n 'COPY --from=builder /app/theo.config.ts ./theo.config.ts',\n 'COPY --from=builder /app/package.json ./package.json',\n `COPY --from=builder /app/${lockfile} ./${lockfile}`,\n 'COPY --from=builder /app/node_modules ./node_modules',\n '',\n 'EXPOSE 3000',\n startCmd,\n '',\n ].join('\\n')\n}\n\nfunction generateDockerignore(): string {\n return [\n 'node_modules',\n '.git',\n '.theo',\n 'dist',\n '.env',\n '.env.*',\n '!.env.example',\n '*.log',\n '.DS_Store',\n '.vscode',\n '.idea',\n 'Dockerfile',\n '.dockerignore',\n '',\n ].join('\\n')\n}\n\nexport async function dockerCommand(options?: { force?: boolean }): Promise<void> {\n const cwd = process.cwd()\n const dockerfilePath = resolve(cwd, 'Dockerfile')\n const dockerignorePath = resolve(cwd, '.dockerignore')\n\n if (existsSync(dockerfilePath) && !options?.force) {\n console.log('\\n ⚠ Dockerfile already exists. Use --force to overwrite.\\n')\n return\n }\n\n const pkgManager = detectPkgManager(cwd)\n writeFileSync(dockerfilePath, generateDockerfile(pkgManager))\n writeFileSync(dockerignorePath, generateDockerignore())\n\n console.log(`\\n ✓ Generated Dockerfile (${pkgManager})`)\n console.log(` ✓ Generated .dockerignore`)\n console.log(`\\n Next steps:`)\n console.log(` docker build -t my-app .`)\n console.log(` docker run -p 3000:3000 my-app\\n`)\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,qBAAqB;AAC1C,SAAS,eAAe;AAExB,SAAS,iBAAiB,KAAsC;AAC9D,MAAI,WAAW,QAAQ,KAAK,gBAAgB,CAAC,EAAG,QAAO;AACvD,MAAI,WAAW,QAAQ,KAAK,WAAW,CAAC,EAAG,QAAO;AAClD,SAAO;AACT;AAEA,SAAS,mBAAmB,YAA6C;AACvE,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR,EAAE,UAAU;AAEZ,QAAM,WAAW;AAAA,IACf,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR,EAAE,UAAU;AAEZ,QAAM,WAAW;AAAA,IACf,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR,EAAE,UAAU;AAEZ,QAAM,WAAW;AAAA,IACf,MAAM;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,EACR,EAAE,UAAU;AAEZ,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,qBAAqB,QAAQ;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,4BAA4B,QAAQ,MAAM,QAAQ;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,uBAA+B;AACtC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,cAAc,SAA8C;AAChF,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,iBAAiB,QAAQ,KAAK,YAAY;AAChD,QAAM,mBAAmB,QAAQ,KAAK,eAAe;AAErD,MAAI,WAAW,cAAc,KAAK,CAAC,SAAS,OAAO;AACjD,YAAQ,IAAI,mEAA8D;AAC1E;AAAA,EACF;AAEA,QAAM,aAAa,iBAAiB,GAAG;AACvC,gBAAc,gBAAgB,mBAAmB,UAAU,CAAC;AAC5D,gBAAc,kBAAkB,qBAAqB,CAAC;AAEtD,UAAQ,IAAI;AAAA,iCAA+B,UAAU,GAAG;AACxD,UAAQ,IAAI,kCAA6B;AACzC,UAAQ,IAAI;AAAA,cAAiB;AAC7B,UAAQ,IAAI,8BAA8B;AAC1C,UAAQ,IAAI;AAAA,CAAsC;AACpD;","names":[]}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/commands/generate.ts
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
5
|
+
import { resolve, dirname } from "path";
|
|
6
|
+
var VALID_TYPES = ["route", "action", "page", "ws"];
|
|
7
|
+
function toKebabCase(name) {
|
|
8
|
+
return /^[a-z][a-z0-9/-]*$/.test(name);
|
|
9
|
+
}
|
|
10
|
+
function toPascalCase(name) {
|
|
11
|
+
return name.split(/[-/]/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
12
|
+
}
|
|
13
|
+
function toCamelCase(name) {
|
|
14
|
+
const pascal = toPascalCase(name);
|
|
15
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
16
|
+
}
|
|
17
|
+
function generateRouteTemplate(name) {
|
|
18
|
+
return [
|
|
19
|
+
`import { defineRoute } from 'theokit/server'`,
|
|
20
|
+
`import { z } from 'zod'`,
|
|
21
|
+
``,
|
|
22
|
+
`export const GET = defineRoute({`,
|
|
23
|
+
` handler: ({ ctx }) => {`,
|
|
24
|
+
` return { message: 'TODO: implement ${name} GET' }`,
|
|
25
|
+
` },`,
|
|
26
|
+
`})`,
|
|
27
|
+
``
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
function generateActionTemplate(name) {
|
|
31
|
+
const camel = toCamelCase(name);
|
|
32
|
+
return [
|
|
33
|
+
`import { defineAction } from 'theokit/server'`,
|
|
34
|
+
`import { z } from 'zod'`,
|
|
35
|
+
``,
|
|
36
|
+
`export const ${camel} = defineAction({`,
|
|
37
|
+
` input: z.object({}),`,
|
|
38
|
+
` handler: ({ input, ctx }) => {`,
|
|
39
|
+
` return { message: 'TODO: implement ${name}' }`,
|
|
40
|
+
` },`,
|
|
41
|
+
`})`,
|
|
42
|
+
``
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
function generatePageTemplate(name) {
|
|
46
|
+
const pascal = toPascalCase(name);
|
|
47
|
+
return [
|
|
48
|
+
`export default function ${pascal}Page() {`,
|
|
49
|
+
` return <h1>${pascal}</h1>`,
|
|
50
|
+
`}`,
|
|
51
|
+
``
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
function generateWsTemplate(name) {
|
|
55
|
+
return [
|
|
56
|
+
`import { defineWebSocket } from 'theokit/server'`,
|
|
57
|
+
``,
|
|
58
|
+
`export default defineWebSocket({`,
|
|
59
|
+
` onMessage(ws, data) {`,
|
|
60
|
+
` ws.send(\`echo: \${data}\`)`,
|
|
61
|
+
` },`,
|
|
62
|
+
`})`,
|
|
63
|
+
``
|
|
64
|
+
].join("\n");
|
|
65
|
+
}
|
|
66
|
+
async function generateCommand(type, name) {
|
|
67
|
+
const cwd = process.cwd();
|
|
68
|
+
if (!existsSync(resolve(cwd, "theo.config.ts")) && !existsSync(resolve(cwd, "theo.config.js"))) {
|
|
69
|
+
throw new Error("Not a Theo project. Run this from a project root with theo.config.ts");
|
|
70
|
+
}
|
|
71
|
+
if (!VALID_TYPES.includes(type)) {
|
|
72
|
+
throw new Error(`Invalid generator type "${type}". Available types: ${VALID_TYPES.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
if (!name || !toKebabCase(name)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
let filePath;
|
|
80
|
+
let content;
|
|
81
|
+
switch (type) {
|
|
82
|
+
case "route":
|
|
83
|
+
filePath = resolve(cwd, "server/routes", `${name}.ts`);
|
|
84
|
+
content = generateRouteTemplate(name);
|
|
85
|
+
break;
|
|
86
|
+
case "action":
|
|
87
|
+
filePath = resolve(cwd, "server/actions", `${name}.ts`);
|
|
88
|
+
content = generateActionTemplate(name);
|
|
89
|
+
break;
|
|
90
|
+
case "page":
|
|
91
|
+
filePath = resolve(cwd, `app/${name}/page.tsx`);
|
|
92
|
+
content = generatePageTemplate(name);
|
|
93
|
+
break;
|
|
94
|
+
case "ws":
|
|
95
|
+
filePath = resolve(cwd, "server/ws", `${name}.ts`);
|
|
96
|
+
content = generateWsTemplate(name);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`Unknown type: ${type}`);
|
|
100
|
+
}
|
|
101
|
+
if (existsSync(filePath)) {
|
|
102
|
+
console.log(`
|
|
103
|
+
\u26A0 ${filePath} already exists. Skipping.
|
|
104
|
+
`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
108
|
+
writeFileSync(filePath, content);
|
|
109
|
+
console.log(`
|
|
110
|
+
\u2713 Created ${type}: ${filePath}
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
generateCommand
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=generate-AA7ZE42F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/generate.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\n\nconst VALID_TYPES = ['route', 'action', 'page', 'ws'] as const\ntype GeneratorType = (typeof VALID_TYPES)[number]\n\nfunction toKebabCase(name: string): boolean {\n return /^[a-z][a-z0-9/-]*$/.test(name)\n}\n\nfunction toPascalCase(name: string): string {\n return name\n .split(/[-/]/)\n .map(s => s.charAt(0).toUpperCase() + s.slice(1))\n .join('')\n}\n\nfunction toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\nfunction generateRouteTemplate(name: string): string {\n return [\n `import { defineRoute } from 'theokit/server'`,\n `import { z } from 'zod'`,\n ``,\n `export const GET = defineRoute({`,\n ` handler: ({ ctx }) => {`,\n ` return { message: 'TODO: implement ${name} GET' }`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateActionTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { defineAction } from 'theokit/server'`,\n `import { z } from 'zod'`,\n ``,\n `export const ${camel} = defineAction({`,\n ` input: z.object({}),`,\n ` handler: ({ input, ctx }) => {`,\n ` return { message: 'TODO: implement ${name}' }`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generatePageTemplate(name: string): string {\n const pascal = toPascalCase(name)\n return [\n `export default function ${pascal}Page() {`,\n ` return <h1>${pascal}</h1>`,\n `}`,\n ``,\n ].join('\\n')\n}\n\nfunction generateWsTemplate(name: string): string {\n return [\n `import { defineWebSocket } from 'theokit/server'`,\n ``,\n `export default defineWebSocket({`,\n ` onMessage(ws, data) {`,\n ` ws.send(\\`echo: \\${data}\\`)`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nexport async function generateCommand(type: string, name: string): Promise<void> {\n const cwd = process.cwd()\n\n // Check if in a Theo project (EC-1)\n if (!existsSync(resolve(cwd, 'theo.config.ts')) && !existsSync(resolve(cwd, 'theo.config.js'))) {\n throw new Error('Not a Theo project. Run this from a project root with theo.config.ts')\n }\n\n // Validate type\n if (!VALID_TYPES.includes(type as GeneratorType)) {\n throw new Error(`Invalid generator type \"${type}\". Available types: ${VALID_TYPES.join(', ')}`)\n }\n\n // Validate name\n if (!name || !toKebabCase(name)) {\n throw new Error(\n `Invalid name \"${name}\". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`,\n )\n }\n\n // Determine file path and content\n let filePath: string\n let content: string\n\n switch (type as GeneratorType) {\n case 'route':\n filePath = resolve(cwd, 'server/routes', `${name}.ts`)\n content = generateRouteTemplate(name)\n break\n case 'action':\n filePath = resolve(cwd, 'server/actions', `${name}.ts`)\n content = generateActionTemplate(name)\n break\n case 'page':\n filePath = resolve(cwd, `app/${name}/page.tsx`)\n content = generatePageTemplate(name)\n break\n case 'ws':\n filePath = resolve(cwd, 'server/ws', `${name}.ts`)\n content = generateWsTemplate(name)\n break\n default:\n throw new Error(`Unknown type: ${type}`)\n }\n\n // Check if file exists\n if (existsSync(filePath)) {\n console.log(`\\n ⚠ ${filePath} already exists. Skipping.\\n`)\n return\n }\n\n // Create directories\n mkdirSync(dirname(filePath), { recursive: true })\n\n // Write file\n writeFileSync(filePath, content)\n\n console.log(`\\n ✓ Created ${type}: ${filePath}\\n`)\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export { theoPlugin } from './vite-plugin/index.js';
|
|
3
|
+
import 'vite';
|
|
4
|
+
import './rate-limit-C6hHXIj1.js';
|
|
5
|
+
import 'node:http';
|
|
6
|
+
|
|
7
|
+
declare const theoConfigSchema: z.ZodObject<{
|
|
8
|
+
appDir: z.ZodDefault<z.ZodString>;
|
|
9
|
+
serverDir: z.ZodDefault<z.ZodString>;
|
|
10
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
ssr: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
windowMs: z.ZodNumber;
|
|
14
|
+
max: z.ZodNumber;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
windowMs: number;
|
|
17
|
+
max: number;
|
|
18
|
+
}, {
|
|
19
|
+
windowMs: number;
|
|
20
|
+
max: number;
|
|
21
|
+
}>>;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
appDir: string;
|
|
24
|
+
serverDir: string;
|
|
25
|
+
port: number;
|
|
26
|
+
ssr: boolean;
|
|
27
|
+
rateLimit?: {
|
|
28
|
+
windowMs: number;
|
|
29
|
+
max: number;
|
|
30
|
+
} | undefined;
|
|
31
|
+
}, {
|
|
32
|
+
appDir?: string | undefined;
|
|
33
|
+
serverDir?: string | undefined;
|
|
34
|
+
port?: number | undefined;
|
|
35
|
+
ssr?: boolean | undefined;
|
|
36
|
+
rateLimit?: {
|
|
37
|
+
windowMs: number;
|
|
38
|
+
max: number;
|
|
39
|
+
} | undefined;
|
|
40
|
+
}>;
|
|
41
|
+
type TheoConfig = z.infer<typeof theoConfigSchema>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Define Theo framework configuration.
|
|
45
|
+
* Identity function — provides type inference for theo.config.ts.
|
|
46
|
+
* Runtime validation happens in loadConfig(), not here.
|
|
47
|
+
*/
|
|
48
|
+
declare function defineConfig(config: Partial<TheoConfig>): Partial<TheoConfig>;
|
|
49
|
+
|
|
50
|
+
declare function loadConfig(dir: string): Promise<TheoConfig>;
|
|
51
|
+
|
|
52
|
+
interface ConfigIssue {
|
|
53
|
+
field: string;
|
|
54
|
+
message: string;
|
|
55
|
+
}
|
|
56
|
+
declare class TheoConfigError extends Error {
|
|
57
|
+
readonly issues: ConfigIssue[];
|
|
58
|
+
readonly configPath: string;
|
|
59
|
+
constructor(issues: ConfigIssue[], configPath: string);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare function validateProjectStructure(rootDir: string): void;
|
|
63
|
+
|
|
64
|
+
declare class TheoProjectError extends Error {
|
|
65
|
+
readonly errors: string[];
|
|
66
|
+
readonly rootDir: string;
|
|
67
|
+
constructor(errors: string[], rootDir: string);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface RouteNode {
|
|
71
|
+
segment: string;
|
|
72
|
+
path: string;
|
|
73
|
+
page?: string;
|
|
74
|
+
layout?: string;
|
|
75
|
+
error?: string;
|
|
76
|
+
loading?: string;
|
|
77
|
+
notFound?: string;
|
|
78
|
+
children: RouteNode[];
|
|
79
|
+
}
|
|
80
|
+
declare function isRouteFile(filename: string): boolean;
|
|
81
|
+
|
|
82
|
+
declare function scanRoutes(appDir: string): RouteNode;
|
|
83
|
+
|
|
84
|
+
declare function generateRouteManifest(tree: RouteNode): string;
|
|
85
|
+
|
|
86
|
+
declare function generateEntryClient(ssr?: boolean): string;
|
|
87
|
+
|
|
88
|
+
export { type ConfigIssue, type RouteNode, type TheoConfig, TheoConfigError, TheoProjectError, defineConfig, generateEntryClient, generateRouteManifest, isRouteFile, loadConfig, scanRoutes, theoConfigSchema, validateProjectStructure };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateEntryClient,
|
|
3
|
+
generateRouteManifest,
|
|
4
|
+
isRouteFile,
|
|
5
|
+
scanRoutes,
|
|
6
|
+
theoPlugin
|
|
7
|
+
} from "./chunk-ATSTRYYT.js";
|
|
8
|
+
import "./chunk-SAVVU5LG.js";
|
|
9
|
+
|
|
10
|
+
// src/config/define-config.ts
|
|
11
|
+
function defineConfig(config) {
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/config/load-config.ts
|
|
16
|
+
import { existsSync } from "fs";
|
|
17
|
+
import { resolve } from "path";
|
|
18
|
+
import { pathToFileURL } from "url";
|
|
19
|
+
|
|
20
|
+
// src/config/schema.ts
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
var rateLimitSchema = z.object({
|
|
23
|
+
windowMs: z.number().min(1),
|
|
24
|
+
max: z.number().int().min(1)
|
|
25
|
+
});
|
|
26
|
+
var theoConfigSchema = z.object({
|
|
27
|
+
appDir: z.string().default("app"),
|
|
28
|
+
serverDir: z.string().default("server"),
|
|
29
|
+
port: z.number().int().min(1).max(65535).default(3e3),
|
|
30
|
+
ssr: z.boolean().default(false),
|
|
31
|
+
rateLimit: rateLimitSchema.optional()
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/config/errors.ts
|
|
35
|
+
var TheoConfigError = class extends Error {
|
|
36
|
+
issues;
|
|
37
|
+
configPath;
|
|
38
|
+
constructor(issues, configPath) {
|
|
39
|
+
const issueLines = issues.map((i) => ` - ${i.field}: ${i.message}`).join("\n");
|
|
40
|
+
super(
|
|
41
|
+
`Invalid theo.config.ts
|
|
42
|
+
|
|
43
|
+
File: ${configPath}
|
|
44
|
+
|
|
45
|
+
` + (issueLines ? ` Issues:
|
|
46
|
+
${issueLines}
|
|
47
|
+
` : "")
|
|
48
|
+
);
|
|
49
|
+
this.name = "TheoConfigError";
|
|
50
|
+
this.issues = issues;
|
|
51
|
+
this.configPath = configPath;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/config/load-config.ts
|
|
56
|
+
var CONFIG_FILE = "theo.config.ts";
|
|
57
|
+
async function loadConfig(dir) {
|
|
58
|
+
const configPath = resolve(dir, CONFIG_FILE);
|
|
59
|
+
if (!existsSync(configPath)) {
|
|
60
|
+
return theoConfigSchema.parse({});
|
|
61
|
+
}
|
|
62
|
+
let mod;
|
|
63
|
+
try {
|
|
64
|
+
mod = await import(pathToFileURL(configPath).href);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw new TheoConfigError(
|
|
67
|
+
[{ field: "_file", message: err.message }],
|
|
68
|
+
configPath
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const userConfig = mod.default;
|
|
72
|
+
if (userConfig == null || typeof userConfig !== "object") {
|
|
73
|
+
throw new TheoConfigError(
|
|
74
|
+
[
|
|
75
|
+
{
|
|
76
|
+
field: "_export",
|
|
77
|
+
message: "theo.config.ts must use export default defineConfig({...})"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
configPath
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const result = theoConfigSchema.safeParse(userConfig);
|
|
84
|
+
if (!result.success) {
|
|
85
|
+
const issues = result.error.issues.map((i) => ({
|
|
86
|
+
field: i.path.join("."),
|
|
87
|
+
message: i.message
|
|
88
|
+
}));
|
|
89
|
+
throw new TheoConfigError(issues, configPath);
|
|
90
|
+
}
|
|
91
|
+
return result.data;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/core/validate-structure.ts
|
|
95
|
+
import { existsSync as existsSync2 } from "fs";
|
|
96
|
+
import { join } from "path";
|
|
97
|
+
|
|
98
|
+
// src/core/errors.ts
|
|
99
|
+
var TheoProjectError = class extends Error {
|
|
100
|
+
errors;
|
|
101
|
+
rootDir;
|
|
102
|
+
constructor(errors, rootDir) {
|
|
103
|
+
const errorLines = errors.map((e) => ` - ${e}`).join("\n");
|
|
104
|
+
super(
|
|
105
|
+
`Invalid Theo project structure
|
|
106
|
+
|
|
107
|
+
Root: ${rootDir}
|
|
108
|
+
|
|
109
|
+
` + (errorLines ? ` Errors:
|
|
110
|
+
${errorLines}
|
|
111
|
+
` : "")
|
|
112
|
+
);
|
|
113
|
+
this.name = "TheoProjectError";
|
|
114
|
+
this.errors = errors;
|
|
115
|
+
this.rootDir = rootDir;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/core/validate-structure.ts
|
|
120
|
+
var REQUIRED_DIRS = [
|
|
121
|
+
{
|
|
122
|
+
path: "app",
|
|
123
|
+
errorMessage: "Missing required directory: app/"
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
var REQUIRED_FILES = [
|
|
127
|
+
{
|
|
128
|
+
path: "theo.config.ts",
|
|
129
|
+
errorMessage: "Missing required file: theo.config.ts"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
path: "package.json",
|
|
133
|
+
errorMessage: "Missing required file: package.json"
|
|
134
|
+
}
|
|
135
|
+
];
|
|
136
|
+
function validateProjectStructure(rootDir) {
|
|
137
|
+
if (!existsSync2(rootDir)) {
|
|
138
|
+
throw new TheoProjectError(
|
|
139
|
+
[`Project directory does not exist: ${rootDir}`],
|
|
140
|
+
rootDir
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const errors = [];
|
|
144
|
+
for (const rule of REQUIRED_DIRS) {
|
|
145
|
+
if (!existsSync2(join(rootDir, rule.path))) {
|
|
146
|
+
errors.push(rule.errorMessage);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const rule of REQUIRED_FILES) {
|
|
150
|
+
if (!existsSync2(join(rootDir, rule.path))) {
|
|
151
|
+
errors.push(rule.errorMessage);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (errors.length > 0) {
|
|
155
|
+
throw new TheoProjectError(errors, rootDir);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
TheoConfigError,
|
|
160
|
+
TheoProjectError,
|
|
161
|
+
defineConfig,
|
|
162
|
+
generateEntryClient,
|
|
163
|
+
generateRouteManifest,
|
|
164
|
+
isRouteFile,
|
|
165
|
+
loadConfig,
|
|
166
|
+
scanRoutes,
|
|
167
|
+
theoConfigSchema,
|
|
168
|
+
theoPlugin,
|
|
169
|
+
validateProjectStructure
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/define-config.ts","../src/config/load-config.ts","../src/config/schema.ts","../src/config/errors.ts","../src/core/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["import type { TheoConfig } from './schema.js'\n\n/**\n * Define Theo framework configuration.\n * Identity function — provides type inference for theo.config.ts.\n * Runtime validation happens in loadConfig(), not here.\n */\nexport function defineConfig(config: Partial<TheoConfig>): Partial<TheoConfig> {\n return config\n}\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { theoConfigSchema } from './schema.js'\nimport { TheoConfigError } from './errors.js'\nimport type { TheoConfig } from './schema.js'\n\nconst CONFIG_FILE = 'theo.config.ts'\n\nexport async function loadConfig(dir: string): Promise<TheoConfig> {\n const configPath = resolve(dir, CONFIG_FILE)\n\n if (!existsSync(configPath)) {\n return theoConfigSchema.parse({})\n }\n\n let mod: Record<string, unknown>\n try {\n mod = await import(pathToFileURL(configPath).href)\n } catch (err) {\n throw new TheoConfigError(\n [{ field: '_file', message: (err as Error).message }],\n configPath,\n )\n }\n\n const userConfig = mod.default\n\n if (userConfig == null || typeof userConfig !== 'object') {\n throw new TheoConfigError(\n [\n {\n field: '_export',\n message:\n 'theo.config.ts must use export default defineConfig({...})',\n },\n ],\n configPath,\n )\n }\n\n const result = theoConfigSchema.safeParse(userConfig)\n\n if (!result.success) {\n const issues = result.error.issues.map((i) => ({\n field: i.path.join('.'),\n message: i.message,\n }))\n throw new TheoConfigError(issues, configPath)\n }\n\n return result.data\n}\n","import { z } from 'zod'\n\nexport const rateLimitSchema = z.object({\n windowMs: z.number().min(1),\n max: z.number().int().min(1),\n})\n\nexport const theoConfigSchema = z.object({\n appDir: z.string().default('app'),\n serverDir: z.string().default('server'),\n port: z.number().int().min(1).max(65535).default(3000),\n ssr: z.boolean().default(false),\n rateLimit: rateLimitSchema.optional(),\n})\n\nexport type TheoConfig = z.infer<typeof theoConfigSchema>\n","export interface ConfigIssue {\n field: string\n message: string\n}\n\nexport class TheoConfigError extends Error {\n public readonly issues: ConfigIssue[]\n public readonly configPath: string\n\n constructor(issues: ConfigIssue[], configPath: string) {\n const issueLines = issues\n .map((i) => ` - ${i.field}: ${i.message}`)\n .join('\\n')\n\n super(\n `Invalid theo.config.ts\\n\\n` +\n ` File: ${configPath}\\n\\n` +\n (issueLines ? ` Issues:\\n${issueLines}\\n` : ''),\n )\n\n this.name = 'TheoConfigError'\n this.issues = issues\n this.configPath = configPath\n }\n}\n","import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { TheoProjectError } from './errors.js'\n\ninterface ValidationRule {\n path: string\n errorMessage: string\n}\n\nconst REQUIRED_DIRS: ValidationRule[] = [\n {\n path: 'app',\n errorMessage: 'Missing required directory: app/',\n },\n]\n\nconst REQUIRED_FILES: ValidationRule[] = [\n {\n path: 'theo.config.ts',\n errorMessage: 'Missing required file: theo.config.ts',\n },\n {\n path: 'package.json',\n errorMessage: 'Missing required file: package.json',\n },\n]\n\nexport function validateProjectStructure(rootDir: string): void {\n if (!existsSync(rootDir)) {\n throw new TheoProjectError(\n [`Project directory does not exist: ${rootDir}`],\n rootDir,\n )\n }\n\n const errors: string[] = []\n\n for (const rule of REQUIRED_DIRS) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n for (const rule of REQUIRED_FILES) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n if (errors.length > 0) {\n throw new TheoProjectError(errors, rootDir)\n }\n}\n","export class TheoProjectError extends Error {\n public readonly errors: string[]\n public readonly rootDir: string\n\n constructor(errors: string[], rootDir: string) {\n const errorLines = errors.map((e) => ` - ${e}`).join('\\n')\n\n super(\n `Invalid Theo project structure\\n\\n` +\n ` Root: ${rootDir}\\n\\n` +\n (errorLines ? ` Errors:\\n${errorLines}\\n` : ''),\n )\n\n this.name = 'TheoProjectError'\n this.errors = errors\n this.rootDir = rootDir\n }\n}\n"],"mappings":"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","names":["existsSync","existsSync"]}
|