theokit 0.1.0-alpha.0 → 0.1.0-alpha.1
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 → build-DX6FLBMO.js} +10 -9
- package/dist/{build-NSKFOAFX.js.map → build-DX6FLBMO.js.map} +1 -1
- package/dist/{chunk-N5YH2UDG.js → chunk-4GCXBW7F.js} +2 -1
- package/dist/{chunk-N5YH2UDG.js.map → chunk-4GCXBW7F.js.map} +1 -1
- package/dist/{chunk-KPU44T6G.js → chunk-BJBRL6MW.js} +2 -1
- package/dist/{chunk-KPU44T6G.js.map → chunk-BJBRL6MW.js.map} +1 -1
- package/dist/{chunk-MMZZBPMX.js → chunk-CDPRKHQ2.js} +2 -1
- package/dist/{chunk-MMZZBPMX.js.map → chunk-CDPRKHQ2.js.map} +1 -1
- package/dist/{chunk-TXMUCDJT.js → chunk-F2FOMARQ.js} +3 -2
- package/dist/{chunk-TXMUCDJT.js.map → chunk-F2FOMARQ.js.map} +1 -1
- package/dist/{chunk-U3OJFWK3.js → chunk-VXMNN5SA.js} +2 -1
- package/dist/{chunk-U3OJFWK3.js.map → chunk-VXMNN5SA.js.map} +1 -1
- package/dist/{chunk-ASGEGAWL.js → chunk-ZHZSF3AC.js} +4 -3
- package/dist/{chunk-ASGEGAWL.js.map → chunk-ZHZSF3AC.js.map} +1 -1
- package/dist/cli/index.js +7 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/{cloudflare-CVGN7FOU.js → cloudflare-UCG6CBPD.js} +6 -5
- package/dist/{cloudflare-CVGN7FOU.js.map → cloudflare-UCG6CBPD.js.map} +1 -1
- package/dist/{dev-7UJK3M2O.js → dev-E3LMBL7C.js} +7 -6
- package/dist/{dev-7UJK3M2O.js.map → dev-E3LMBL7C.js.map} +1 -1
- package/dist/{docker-M253W54T.js → docker-NQ5NM4IW.js} +2 -1
- package/dist/{docker-M253W54T.js.map → docker-NQ5NM4IW.js.map} +1 -1
- package/dist/{generate-AA7ZE42F.js → generate-MYEUR7I7.js} +2 -1
- package/dist/{generate-AA7ZE42F.js.map → generate-MYEUR7I7.js.map} +1 -1
- package/dist/{routes-YP357VAC.js → routes-SDRMNHNM.js} +5 -4
- package/dist/{routes-YP357VAC.js.map → routes-SDRMNHNM.js.map} +1 -1
- package/dist/{start-BIS3RCFQ.js → start-GJTYZIHY.js} +5 -4
- package/dist/{start-BIS3RCFQ.js.map → start-GJTYZIHY.js.map} +1 -1
- package/dist/{vercel-747SR2FB.js → vercel-K4OINXCD.js} +6 -5
- package/dist/{vercel-747SR2FB.js.map → vercel-K4OINXCD.js.map} +1 -1
- package/package.json +2 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
nodeAdapter
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-F2FOMARQ.js";
|
|
6
|
+
import "./chunk-ZHZSF3AC.js";
|
|
7
|
+
import "./chunk-CDPRKHQ2.js";
|
|
7
8
|
import {
|
|
8
9
|
validateProjectStructure
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BJBRL6MW.js";
|
|
10
11
|
import {
|
|
11
12
|
loadConfig
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-4GCXBW7F.js";
|
|
14
|
+
import "./chunk-VXMNN5SA.js";
|
|
14
15
|
|
|
15
16
|
// src/adapters/types.ts
|
|
16
17
|
var VALID_TARGETS = ["node", "vercel", "cloudflare"];
|
|
@@ -32,10 +33,10 @@ async function buildCommand(options) {
|
|
|
32
33
|
if (target === "node") {
|
|
33
34
|
await nodeAdapter.build(config, cwd);
|
|
34
35
|
} else if (target === "vercel") {
|
|
35
|
-
const { vercelAdapter } = await import("./vercel-
|
|
36
|
+
const { vercelAdapter } = await import("./vercel-K4OINXCD.js");
|
|
36
37
|
await vercelAdapter.build(config, cwd);
|
|
37
38
|
} else if (target === "cloudflare") {
|
|
38
|
-
const { cloudflareAdapter } = await import("./cloudflare-
|
|
39
|
+
const { cloudflareAdapter } = await import("./cloudflare-UCG6CBPD.js");
|
|
39
40
|
await cloudflareAdapter.build(config, cwd);
|
|
40
41
|
}
|
|
41
42
|
const ssrNote = config.ssr ? " (SSR)" : "";
|
|
@@ -46,4 +47,4 @@ async function buildCommand(options) {
|
|
|
46
47
|
export {
|
|
47
48
|
buildCommand
|
|
48
49
|
};
|
|
49
|
-
//# sourceMappingURL=build-
|
|
50
|
+
//# sourceMappingURL=build-DX6FLBMO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/types.ts","../src/cli/commands/build.ts"],"sourcesContent":["import type { TheoConfig } from '../config/schema.js'\n\nexport interface DeployAdapter {\n name: string\n build(config: TheoConfig, cwd: string): Promise<void>\n}\n\nexport type BuildTarget = 'node' | 'vercel' | 'cloudflare'\n\nexport const VALID_TARGETS: BuildTarget[] = ['node', 'vercel', 'cloudflare']\n","import { loadConfig } from '../../config/load-config.js'\nimport { validateProjectStructure } from '../../core/validate-structure.js'\nimport { VALID_TARGETS, type BuildTarget } from '../../adapters/types.js'\nimport { nodeAdapter } from '../../adapters/node.js'\n\nexport async function buildCommand(options?: { target?: string }): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n const target = (options?.target ?? 'node') as BuildTarget\n\n if (!VALID_TARGETS.includes(target)) {\n throw new Error(\n `Invalid build target \"${target}\". Available targets: ${VALID_TARGETS.join(', ')}`,\n )\n }\n\n console.log(`\\n Building for ${target}...\\n`)\n\n if (target === 'node') {\n await nodeAdapter.build(config, cwd)\n } else if (target === 'vercel') {\n const { vercelAdapter } = await import('../../adapters/vercel.js')\n await vercelAdapter.build(config, cwd)\n } else if (target === 'cloudflare') {\n const { cloudflareAdapter } = await import('../../adapters/cloudflare.js')\n await cloudflareAdapter.build(config, cwd)\n }\n\n const ssrNote = config.ssr ? ' (SSR)' : ''\n console.log(`\\n ✓ Build complete → ${target}${ssrNote}\\n`)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/types.ts","../src/cli/commands/build.ts"],"sourcesContent":["import type { TheoConfig } from '../config/schema.js'\n\nexport interface DeployAdapter {\n name: string\n build(config: TheoConfig, cwd: string): Promise<void>\n}\n\nexport type BuildTarget = 'node' | 'vercel' | 'cloudflare'\n\nexport const VALID_TARGETS: BuildTarget[] = ['node', 'vercel', 'cloudflare']\n","import { loadConfig } from '../../config/load-config.js'\nimport { validateProjectStructure } from '../../core/validate-structure.js'\nimport { VALID_TARGETS, type BuildTarget } from '../../adapters/types.js'\nimport { nodeAdapter } from '../../adapters/node.js'\n\nexport async function buildCommand(options?: { target?: string }): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n const target = (options?.target ?? 'node') as BuildTarget\n\n if (!VALID_TARGETS.includes(target)) {\n throw new Error(\n `Invalid build target \"${target}\". Available targets: ${VALID_TARGETS.join(', ')}`,\n )\n }\n\n console.log(`\\n Building for ${target}...\\n`)\n\n if (target === 'node') {\n await nodeAdapter.build(config, cwd)\n } else if (target === 'vercel') {\n const { vercelAdapter } = await import('../../adapters/vercel.js')\n await vercelAdapter.build(config, cwd)\n } else if (target === 'cloudflare') {\n const { cloudflareAdapter } = await import('../../adapters/cloudflare.js')\n await cloudflareAdapter.build(config, cwd)\n }\n\n const ssrNote = config.ssr ? ' (SSR)' : ''\n console.log(`\\n ✓ Build complete → ${target}${ssrNote}\\n`)\n}\n"],"mappings":";;;;;;;;;;;;;;;;AASO,IAAM,gBAA+B,CAAC,QAAQ,UAAU,YAAY;;;ACJ3E,eAAsB,aAAa,SAA8C;AAC/E,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,2BAAyB,GAAG;AAE5B,QAAM,SAAU,SAAS,UAAU;AAEnC,MAAI,CAAC,cAAc,SAAS,MAAM,GAAG;AACnC,UAAM,IAAI;AAAA,MACR,yBAAyB,MAAM,yBAAyB,cAAc,KAAK,IAAI,CAAC;AAAA,IAClF;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,iBAAoB,MAAM;AAAA,CAAO;AAE7C,MAAI,WAAW,QAAQ;AACrB,UAAM,YAAY,MAAM,QAAQ,GAAG;AAAA,EACrC,WAAW,WAAW,UAAU;AAC9B,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAA0B;AACjE,UAAM,cAAc,MAAM,QAAQ,GAAG;AAAA,EACvC,WAAW,WAAW,cAAc;AAClC,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,0BAA8B;AACzE,UAAM,kBAAkB,MAAM,QAAQ,GAAG;AAAA,EAC3C;AAEA,QAAM,UAAU,OAAO,MAAM,WAAW;AACxC,UAAQ,IAAI;AAAA,iCAA0B,MAAM,GAAG,OAAO;AAAA,CAAI;AAC5D;","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/config/load-config.ts
|
|
4
5
|
import { existsSync } from "fs";
|
|
@@ -82,4 +83,4 @@ async function loadConfig(dir) {
|
|
|
82
83
|
export {
|
|
83
84
|
loadConfig
|
|
84
85
|
};
|
|
85
|
-
//# sourceMappingURL=chunk-
|
|
86
|
+
//# sourceMappingURL=chunk-4GCXBW7F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/load-config.ts","../src/config/schema.ts","../src/config/errors.ts"],"sourcesContent":["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"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/config/load-config.ts","../src/config/schema.ts","../src/config/errors.ts"],"sourcesContent":["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"],"mappings":";;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,qBAAqB;;;ACF9B,SAAS,SAAS;AAEX,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAC7B,CAAC;AAEM,IAAM,mBAAmB,EAAE,OAAO;AAAA,EACvC,QAAQ,EAAE,OAAO,EAAE,QAAQ,KAAK;AAAA,EAChC,WAAW,EAAE,OAAO,EAAE,QAAQ,QAAQ;AAAA,EACtC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,EAAE,QAAQ,GAAI;AAAA,EACrD,KAAK,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAC9B,WAAW,gBAAgB,SAAS;AACtC,CAAC;;;ACRM,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzB;AAAA,EACA;AAAA,EAEhB,YAAY,QAAuB,YAAoB;AACrD,UAAM,aAAa,OAChB,IAAI,CAAC,MAAM,OAAO,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE,EACzC,KAAK,IAAI;AAEZ;AAAA,MACE;AAAA;AAAA,UACa,UAAU;AAAA;AAAA,KACpB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,aAAa;AAAA,EACpB;AACF;;;AFjBA,IAAM,cAAc;AAEpB,eAAsB,WAAW,KAAkC;AACjE,QAAM,aAAa,QAAQ,KAAK,WAAW;AAE3C,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAO,iBAAiB,MAAM,CAAC,CAAC;AAAA,EAClC;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,OAAO,cAAc,UAAU,EAAE;AAAA,EAC/C,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,CAAC,EAAE,OAAO,SAAS,SAAU,IAAc,QAAQ,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI;AAEvB,MAAI,cAAc,QAAQ,OAAO,eAAe,UAAU;AACxD,UAAM,IAAI;AAAA,MACR;AAAA,QACE;AAAA,UACE,OAAO;AAAA,UACP,SACE;AAAA,QACJ;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,iBAAiB,UAAU,UAAU;AAEpD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,OAAO;AAAA,MAC7C,OAAO,EAAE,KAAK,KAAK,GAAG;AAAA,MACtB,SAAS,EAAE;AAAA,IACb,EAAE;AACF,UAAM,IAAI,gBAAgB,QAAQ,UAAU;AAAA,EAC9C;AAEA,SAAO,OAAO;AAChB;","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/core/validate-structure.ts
|
|
4
5
|
import { existsSync } from "fs";
|
|
@@ -68,4 +69,4 @@ function validateProjectStructure(rootDir) {
|
|
|
68
69
|
export {
|
|
69
70
|
validateProjectStructure
|
|
70
71
|
};
|
|
71
|
-
//# sourceMappingURL=chunk-
|
|
72
|
+
//# sourceMappingURL=chunk-BJBRL6MW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["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":"
|
|
1
|
+
{"version":3,"sources":["../src/core/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["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":";;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;;;ACDd,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAEhB,YAAY,QAAkB,SAAiB;AAC7C,UAAM,aAAa,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAE1D;AAAA,MACE;AAAA;AAAA,UACa,OAAO;AAAA;AAAA,KACjB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;;;ADRA,IAAM,gBAAkC;AAAA,EACtC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEA,IAAM,iBAAmC;AAAA,EACvC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEO,SAAS,yBAAyB,SAAuB;AAC9D,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,UAAM,IAAI;AAAA,MACR,CAAC,qCAAqC,OAAO,EAAE;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAE1B,aAAW,QAAQ,eAAe;AAChC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,iBAAiB,QAAQ,OAAO;AAAA,EAC5C;AACF;","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/server/middleware-runner.ts
|
|
4
5
|
import { existsSync } from "fs";
|
|
@@ -332,4 +333,4 @@ export {
|
|
|
332
333
|
createRateLimiter,
|
|
333
334
|
executeAction
|
|
334
335
|
};
|
|
335
|
-
//# sourceMappingURL=chunk-
|
|
336
|
+
//# sourceMappingURL=chunk-CDPRKHQ2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/middleware-runner.ts","../src/server/auth.ts","../src/server/execute.ts","../src/server/module-loader.ts","../src/server/logger.ts","../src/server/rate-limit.ts","../src/server/csrf.ts","../src/server/action-execute.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface MiddlewareResult {\n ctx: unknown\n aborted: boolean\n}\n\nexport async function runMiddlewareAndContext(\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir: string,\n): Promise<MiddlewareResult> {\n // 1. Run middleware (if exists)\n const middlewarePath = join(serverDir, 'middleware.ts')\n if (existsSync(middlewarePath)) {\n const mod = await loadModule(middlewarePath)\n const mw = mod.default\n if (typeof mw === 'function') {\n let nextCalled = false\n await mw(req, res, async () => {\n nextCalled = true\n })\n if (!nextCalled || res.writableEnded) {\n return { ctx: {}, aborted: true }\n }\n }\n }\n\n // 2. Create context (if exists)\n let ctx: unknown = {}\n const contextPath = join(serverDir, 'context.ts')\n if (existsSync(contextPath)) {\n const mod = await loadModule(contextPath)\n if (typeof mod.createContext === 'function') {\n ctx = await (mod.createContext as Function)({ request: req, response: res })\n }\n }\n\n return { ctx, aborted: false }\n}\n","export class AuthRequiredError extends Error {\n code = 'AUTH_REQUIRED' as const\n status = 401\n\n constructor(message = 'Authentication required') {\n super(message)\n this.name = 'AuthRequiredError'\n }\n}\n\nexport function requireAuth<T>(session: T | null | undefined): asserts session is T {\n if (session === null || session === undefined) {\n throw new AuthRequiredError()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { ServerRouteNode } from './match.js'\nimport type { LoadModule } from './module-loader.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nconst METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH']\n\nexport function sendJson(\n res: ServerResponse,\n data: unknown,\n status = 200,\n): void {\n const body = JSON.stringify(data)\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n })\n res.end(body)\n}\n\nexport function sendError(\n res: ServerResponse,\n code: string,\n message: string,\n status: number,\n issues?: unknown[],\n requestId?: string,\n): void {\n const errorMessage =\n code === 'INTERNAL_ERROR' && process.env.NODE_ENV === 'production'\n ? 'Internal server error'\n : message\n\n if (code === 'INTERNAL_ERROR') {\n console.error(`[${requestId ?? 'no-id'}] ${message}`)\n }\n\n sendJson(\n res,\n {\n error: {\n code,\n message: errorMessage,\n ...(requestId ? { requestId } : {}),\n ...(issues ? { issues } : {}),\n },\n },\n status,\n )\n}\n\nexport function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const method = req.method?.toUpperCase() ?? 'GET'\n if (!METHODS_WITH_BODY.includes(method)) {\n return resolve(undefined)\n }\n\n const contentType = req.headers['content-type'] ?? ''\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString()\n if (!raw) return resolve(undefined)\n\n if (!contentType.includes('application/json')) {\n return reject(new Error('Expected Content-Type: application/json'))\n }\n\n try {\n resolve(JSON.parse(raw))\n } catch {\n reject(new Error('Invalid JSON body'))\n }\n })\n req.on('error', reject)\n })\n}\n\nexport async function executeRoute(\n route: ServerRouteNode,\n method: string,\n params: Record<string, string>,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n const mod = await loadModule(route.filePath)\n const routeConfig = mod[method]\n\n if (!routeConfig) {\n sendError(res, 'METHOD_NOT_ALLOWED', `Method ${method} not allowed`, 405, undefined, requestId)\n return\n }\n\n const handler = typeof routeConfig === 'function' ? routeConfig : (routeConfig as Record<string, unknown>).handler\n if (typeof handler !== 'function') {\n sendError(res, 'INTERNAL_ERROR', 'Route handler is not a function', 500, undefined, requestId)\n return\n }\n\n // Parse query\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\n const query: Record<string, string> = Object.fromEntries(url.searchParams)\n\n // Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // Zod validation\n const rc = routeConfig as Record<string, unknown>\n if (rc.query && typeof (rc.query as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.query as { safeParse: Function }).safeParse(query)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid query parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(query, result.data)\n }\n\n if (rc.body && typeof (rc.body as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.body as { safeParse: Function }).safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid request body', 400, result.error.issues, requestId)\n return\n }\n body = result.data\n }\n\n if (rc.params && typeof (rc.params as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.params as { safeParse: Function }).safeParse(params)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid route parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(params, result.data)\n }\n\n // Execute handler\n const handlerResult = await handler({ query, body, params, request: req, ctx })\n\n // Handle result\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, (rc.status as number) ?? 204)\n return\n }\n\n if (handlerResult instanceof Response) {\n res.writeHead(handlerResult.status, Object.fromEntries(handlerResult.headers))\n\n if (handlerResult.body) {\n const reader = handlerResult.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(value)\n }\n } catch {\n // Stream error after headers sent — just close the response\n }\n }\n\n res.end()\n return\n }\n\n sendJson(res, handlerResult, (rc.status as number) ?? 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n","import type { ViteDevServer } from 'vite'\nimport { pathToFileURL } from 'node:url'\n\nexport type LoadModule = (path: string) => Promise<Record<string, unknown>>\n\nexport function createViteLoader(vite: ViteDevServer): LoadModule {\n return (path) => vite.ssrLoadModule(path) as Promise<Record<string, unknown>>\n}\n\nexport function createProductionLoader(): LoadModule {\n return async (path) => {\n const url = pathToFileURL(path).href\n return import(url) as Promise<Record<string, unknown>>\n }\n}\n","export interface RequestLog {\n level: string\n method: string\n url: string\n status: number\n duration: number\n requestId: string\n timestamp: string\n}\n\nexport type LoggerFn = (log: RequestLog) => void\n\nconst defaultLogger: LoggerFn = (log) => {\n console.log(JSON.stringify(log))\n}\n\nexport function logRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLogger\n logger(log)\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface RateLimitConfig {\n windowMs: number\n max: number\n}\n\nexport interface RateLimitResult {\n limited: boolean\n headers: Record<string, string>\n}\n\ninterface StoreEntry {\n count: number\n resetAt: number\n}\n\nexport function createRateLimiter(config: RateLimitConfig) {\n const store = new Map<string, StoreEntry>()\n let checkCount = 0\n\n return function checkRateLimit(req: IncomingMessage): RateLimitResult {\n const key = req.socket?.remoteAddress ?? 'unknown'\n const now = Date.now()\n\n // Periodic cleanup (EC-1): every 1000 checks, remove expired entries\n if (++checkCount % 1000 === 0) {\n for (const [k, v] of store) {\n if (v.resetAt < now) store.delete(k)\n }\n }\n\n const entry = store.get(key)\n\n if (!entry || now > entry.resetAt) {\n store.set(key, { count: 1, resetAt: now + config.windowMs })\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - 1),\n },\n }\n }\n\n entry.count++\n\n if (entry.count > config.max) {\n const retryAfter = Math.ceil((entry.resetAt - now) / 1000)\n return {\n limited: true,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': '0',\n 'Retry-After': String(retryAfter),\n },\n }\n }\n\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - entry.count),\n },\n }\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport function validateCsrf(\n req: IncomingMessage,\n): { valid: true } | { valid: false; reason: string } {\n // 1. Custom header must be present (primary defense)\n if (req.headers['x-theo-action'] !== '1') {\n return { valid: false, reason: 'Missing X-Theo-Action header' }\n }\n\n // 2. Origin matching (secondary defense)\n const origin = req.headers['origin']\n if (!origin) {\n // Browsers omit Origin for same-origin requests — treat as valid\n return { valid: true }\n }\n\n const host = req.headers['host']\n if (!host) {\n return { valid: true }\n }\n\n try {\n const originHost = new URL(origin).host\n if (originHost !== host) {\n return { valid: false, reason: `Origin ${origin} does not match host ${host}` }\n }\n } catch {\n return { valid: false, reason: `Invalid origin: ${origin}` }\n }\n\n return { valid: true }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { validateCsrf } from './csrf.js'\nimport { parseBody, sendJson, sendError } from './execute.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nexport async function executeAction(\n filePath: string,\n exportName: string,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // 1. Only POST\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'POST') {\n sendError(res, 'METHOD_NOT_ALLOWED', 'Actions only accept POST', 405, undefined, requestId)\n return\n }\n\n // 2. CSRF validation\n const csrf = validateCsrf(req)\n if (!csrf.valid) {\n sendError(res, 'FORBIDDEN', csrf.reason, 403, undefined, requestId)\n return\n }\n\n // 3. Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n // 4. Load module\n const mod = await loadModule(filePath)\n\n // 5. Find export\n const actionConfig = mod[exportName] as Record<string, unknown> | undefined\n if (!actionConfig || typeof actionConfig.handler !== 'function' || !actionConfig.input) {\n sendError(res, 'NOT_FOUND', `Action \"${exportName}\" not found`, 404, undefined, requestId)\n return\n }\n\n // 6. Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // 7. Validate input with Zod\n const input = actionConfig.input as { safeParse: (v: unknown) => { success: boolean; data?: unknown; error?: { issues: unknown[] } } }\n const result = input.safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid action input', 400, result.error?.issues, requestId)\n return\n }\n\n // 8. Execute handler\n const handlerResult = await (actionConfig.handler as Function)({ input: result.data, ctx })\n\n // 9. Send response\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, 204)\n return\n }\n\n sendJson(res, handlerResult, 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n"],"mappings":"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","names":["result"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/middleware-runner.ts","../src/server/auth.ts","../src/server/execute.ts","../src/server/module-loader.ts","../src/server/logger.ts","../src/server/rate-limit.ts","../src/server/csrf.ts","../src/server/action-execute.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport interface MiddlewareResult {\n ctx: unknown\n aborted: boolean\n}\n\nexport async function runMiddlewareAndContext(\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir: string,\n): Promise<MiddlewareResult> {\n // 1. Run middleware (if exists)\n const middlewarePath = join(serverDir, 'middleware.ts')\n if (existsSync(middlewarePath)) {\n const mod = await loadModule(middlewarePath)\n const mw = mod.default\n if (typeof mw === 'function') {\n let nextCalled = false\n await mw(req, res, async () => {\n nextCalled = true\n })\n if (!nextCalled || res.writableEnded) {\n return { ctx: {}, aborted: true }\n }\n }\n }\n\n // 2. Create context (if exists)\n let ctx: unknown = {}\n const contextPath = join(serverDir, 'context.ts')\n if (existsSync(contextPath)) {\n const mod = await loadModule(contextPath)\n if (typeof mod.createContext === 'function') {\n ctx = await (mod.createContext as Function)({ request: req, response: res })\n }\n }\n\n return { ctx, aborted: false }\n}\n","export class AuthRequiredError extends Error {\n code = 'AUTH_REQUIRED' as const\n status = 401\n\n constructor(message = 'Authentication required') {\n super(message)\n this.name = 'AuthRequiredError'\n }\n}\n\nexport function requireAuth<T>(session: T | null | undefined): asserts session is T {\n if (session === null || session === undefined) {\n throw new AuthRequiredError()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { ServerRouteNode } from './match.js'\nimport type { LoadModule } from './module-loader.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nconst METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH']\n\nexport function sendJson(\n res: ServerResponse,\n data: unknown,\n status = 200,\n): void {\n const body = JSON.stringify(data)\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body),\n })\n res.end(body)\n}\n\nexport function sendError(\n res: ServerResponse,\n code: string,\n message: string,\n status: number,\n issues?: unknown[],\n requestId?: string,\n): void {\n const errorMessage =\n code === 'INTERNAL_ERROR' && process.env.NODE_ENV === 'production'\n ? 'Internal server error'\n : message\n\n if (code === 'INTERNAL_ERROR') {\n console.error(`[${requestId ?? 'no-id'}] ${message}`)\n }\n\n sendJson(\n res,\n {\n error: {\n code,\n message: errorMessage,\n ...(requestId ? { requestId } : {}),\n ...(issues ? { issues } : {}),\n },\n },\n status,\n )\n}\n\nexport function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const method = req.method?.toUpperCase() ?? 'GET'\n if (!METHODS_WITH_BODY.includes(method)) {\n return resolve(undefined)\n }\n\n const contentType = req.headers['content-type'] ?? ''\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString()\n if (!raw) return resolve(undefined)\n\n if (!contentType.includes('application/json')) {\n return reject(new Error('Expected Content-Type: application/json'))\n }\n\n try {\n resolve(JSON.parse(raw))\n } catch {\n reject(new Error('Invalid JSON body'))\n }\n })\n req.on('error', reject)\n })\n}\n\nexport async function executeRoute(\n route: ServerRouteNode,\n method: string,\n params: Record<string, string>,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n const mod = await loadModule(route.filePath)\n const routeConfig = mod[method]\n\n if (!routeConfig) {\n sendError(res, 'METHOD_NOT_ALLOWED', `Method ${method} not allowed`, 405, undefined, requestId)\n return\n }\n\n const handler = typeof routeConfig === 'function' ? routeConfig : (routeConfig as Record<string, unknown>).handler\n if (typeof handler !== 'function') {\n sendError(res, 'INTERNAL_ERROR', 'Route handler is not a function', 500, undefined, requestId)\n return\n }\n\n // Parse query\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\n const query: Record<string, string> = Object.fromEntries(url.searchParams)\n\n // Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // Zod validation\n const rc = routeConfig as Record<string, unknown>\n if (rc.query && typeof (rc.query as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.query as { safeParse: Function }).safeParse(query)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid query parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(query, result.data)\n }\n\n if (rc.body && typeof (rc.body as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.body as { safeParse: Function }).safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid request body', 400, result.error.issues, requestId)\n return\n }\n body = result.data\n }\n\n if (rc.params && typeof (rc.params as { safeParse: Function }).safeParse === 'function') {\n const result = (rc.params as { safeParse: Function }).safeParse(params)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid route parameters', 400, result.error.issues, requestId)\n return\n }\n Object.assign(params, result.data)\n }\n\n // Execute handler\n const handlerResult = await handler({ query, body, params, request: req, ctx })\n\n // Handle result\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, (rc.status as number) ?? 204)\n return\n }\n\n if (handlerResult instanceof Response) {\n res.writeHead(handlerResult.status, Object.fromEntries(handlerResult.headers))\n\n if (handlerResult.body) {\n const reader = handlerResult.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(value)\n }\n } catch {\n // Stream error after headers sent — just close the response\n }\n }\n\n res.end()\n return\n }\n\n sendJson(res, handlerResult, (rc.status as number) ?? 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n","import type { ViteDevServer } from 'vite'\nimport { pathToFileURL } from 'node:url'\n\nexport type LoadModule = (path: string) => Promise<Record<string, unknown>>\n\nexport function createViteLoader(vite: ViteDevServer): LoadModule {\n return (path) => vite.ssrLoadModule(path) as Promise<Record<string, unknown>>\n}\n\nexport function createProductionLoader(): LoadModule {\n return async (path) => {\n const url = pathToFileURL(path).href\n return import(url) as Promise<Record<string, unknown>>\n }\n}\n","export interface RequestLog {\n level: string\n method: string\n url: string\n status: number\n duration: number\n requestId: string\n timestamp: string\n}\n\nexport type LoggerFn = (log: RequestLog) => void\n\nconst defaultLogger: LoggerFn = (log) => {\n console.log(JSON.stringify(log))\n}\n\nexport function logRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLogger\n logger(log)\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface RateLimitConfig {\n windowMs: number\n max: number\n}\n\nexport interface RateLimitResult {\n limited: boolean\n headers: Record<string, string>\n}\n\ninterface StoreEntry {\n count: number\n resetAt: number\n}\n\nexport function createRateLimiter(config: RateLimitConfig) {\n const store = new Map<string, StoreEntry>()\n let checkCount = 0\n\n return function checkRateLimit(req: IncomingMessage): RateLimitResult {\n const key = req.socket?.remoteAddress ?? 'unknown'\n const now = Date.now()\n\n // Periodic cleanup (EC-1): every 1000 checks, remove expired entries\n if (++checkCount % 1000 === 0) {\n for (const [k, v] of store) {\n if (v.resetAt < now) store.delete(k)\n }\n }\n\n const entry = store.get(key)\n\n if (!entry || now > entry.resetAt) {\n store.set(key, { count: 1, resetAt: now + config.windowMs })\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - 1),\n },\n }\n }\n\n entry.count++\n\n if (entry.count > config.max) {\n const retryAfter = Math.ceil((entry.resetAt - now) / 1000)\n return {\n limited: true,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': '0',\n 'Retry-After': String(retryAfter),\n },\n }\n }\n\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': String(config.max - entry.count),\n },\n }\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport function validateCsrf(\n req: IncomingMessage,\n): { valid: true } | { valid: false; reason: string } {\n // 1. Custom header must be present (primary defense)\n if (req.headers['x-theo-action'] !== '1') {\n return { valid: false, reason: 'Missing X-Theo-Action header' }\n }\n\n // 2. Origin matching (secondary defense)\n const origin = req.headers['origin']\n if (!origin) {\n // Browsers omit Origin for same-origin requests — treat as valid\n return { valid: true }\n }\n\n const host = req.headers['host']\n if (!host) {\n return { valid: true }\n }\n\n try {\n const originHost = new URL(origin).host\n if (originHost !== host) {\n return { valid: false, reason: `Origin ${origin} does not match host ${host}` }\n }\n } catch {\n return { valid: false, reason: `Invalid origin: ${origin}` }\n }\n\n return { valid: true }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { LoadModule } from './module-loader.js'\nimport { validateCsrf } from './csrf.js'\nimport { parseBody, sendJson, sendError } from './execute.js'\nimport { runMiddlewareAndContext } from './middleware-runner.js'\nimport { AuthRequiredError } from './auth.js'\n\nexport async function executeAction(\n filePath: string,\n exportName: string,\n req: IncomingMessage,\n res: ServerResponse,\n loadModule: LoadModule,\n serverDir?: string,\n requestId?: string,\n): Promise<void> {\n try {\n // 1. Only POST\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'POST') {\n sendError(res, 'METHOD_NOT_ALLOWED', 'Actions only accept POST', 405, undefined, requestId)\n return\n }\n\n // 2. CSRF validation\n const csrf = validateCsrf(req)\n if (!csrf.valid) {\n sendError(res, 'FORBIDDEN', csrf.reason, 403, undefined, requestId)\n return\n }\n\n // 3. Run middleware + context pipeline\n let ctx: unknown = {}\n if (serverDir) {\n const result = await runMiddlewareAndContext(req, res, loadModule, serverDir)\n if (result.aborted) return\n ctx = result.ctx\n }\n\n // 4. Load module\n const mod = await loadModule(filePath)\n\n // 5. Find export\n const actionConfig = mod[exportName] as Record<string, unknown> | undefined\n if (!actionConfig || typeof actionConfig.handler !== 'function' || !actionConfig.input) {\n sendError(res, 'NOT_FOUND', `Action \"${exportName}\" not found`, 404, undefined, requestId)\n return\n }\n\n // 6. Parse body\n let body: unknown\n try {\n body = await parseBody(req)\n } catch (err) {\n sendError(res, 'VALIDATION_ERROR', (err as Error).message, 400, undefined, requestId)\n return\n }\n\n // 7. Validate input with Zod\n const input = actionConfig.input as { safeParse: (v: unknown) => { success: boolean; data?: unknown; error?: { issues: unknown[] } } }\n const result = input.safeParse(body)\n if (!result.success) {\n sendError(res, 'VALIDATION_ERROR', 'Invalid action input', 400, result.error?.issues, requestId)\n return\n }\n\n // 8. Execute handler\n const handlerResult = await (actionConfig.handler as Function)({ input: result.data, ctx })\n\n // 9. Send response\n if (handlerResult === undefined || handlerResult === null) {\n sendJson(res, null, 204)\n return\n }\n\n sendJson(res, handlerResult, 200)\n } catch (err) {\n if (err instanceof AuthRequiredError) {\n sendError(res, err.code, err.message, err.status, undefined, requestId)\n return\n }\n sendError(res, 'INTERNAL_ERROR', (err as Error).message ?? 'Internal server error', 500, undefined, requestId)\n }\n}\n"],"mappings":";;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAOrB,eAAsB,wBACpB,KACA,KACA,YACA,WAC2B;AAE3B,QAAM,iBAAiB,KAAK,WAAW,eAAe;AACtD,MAAI,WAAW,cAAc,GAAG;AAC9B,UAAM,MAAM,MAAM,WAAW,cAAc;AAC3C,UAAM,KAAK,IAAI;AACf,QAAI,OAAO,OAAO,YAAY;AAC5B,UAAI,aAAa;AACjB,YAAM,GAAG,KAAK,KAAK,YAAY;AAC7B,qBAAa;AAAA,MACf,CAAC;AACD,UAAI,CAAC,cAAc,IAAI,eAAe;AACpC,eAAO,EAAE,KAAK,CAAC,GAAG,SAAS,KAAK;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAGA,MAAI,MAAe,CAAC;AACpB,QAAM,cAAc,KAAK,WAAW,YAAY;AAChD,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,MAAM,MAAM,WAAW,WAAW;AACxC,QAAI,OAAO,IAAI,kBAAkB,YAAY;AAC3C,YAAM,MAAO,IAAI,cAA2B,EAAE,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO,EAAE,KAAK,SAAS,MAAM;AAC/B;;;AC3CO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,OAAO;AAAA,EACP,SAAS;AAAA,EAET,YAAY,UAAU,2BAA2B;AAC/C,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACFA,IAAM,oBAAoB,CAAC,QAAQ,OAAO,OAAO;AAE1C,SAAS,SACd,KACA,MACA,SAAS,KACH;AACN,QAAM,OAAO,KAAK,UAAU,IAAI;AAChC,MAAI,UAAU,QAAQ;AAAA,IACpB,gBAAgB;AAAA,IAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,EAC1C,CAAC;AACD,MAAI,IAAI,IAAI;AACd;AAEO,SAAS,UACd,KACA,MACA,SACA,QACA,QACA,WACM;AACN,QAAM,eACJ,SAAS,oBAAoB,QAAQ,IAAI,aAAa,eAClD,0BACA;AAEN,MAAI,SAAS,kBAAkB;AAC7B,YAAQ,MAAM,IAAI,aAAa,OAAO,KAAK,OAAO,EAAE;AAAA,EACtD;AAEA;AAAA,IACE;AAAA,IACA;AAAA,MACE,OAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,QACjC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,UAAU,KAAwC;AAChE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,QAAQ,YAAY,KAAK;AAC5C,QAAI,CAAC,kBAAkB,SAAS,MAAM,GAAG;AACvC,aAAO,QAAQ,MAAS;AAAA,IAC1B;AAEA,UAAM,cAAc,IAAI,QAAQ,cAAc,KAAK;AACnD,UAAM,SAAmB,CAAC;AAE1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM;AAClB,YAAM,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS;AAC3C,UAAI,CAAC,IAAK,QAAO,QAAQ,MAAS;AAElC,UAAI,CAAC,YAAY,SAAS,kBAAkB,GAAG;AAC7C,eAAO,OAAO,IAAI,MAAM,yCAAyC,CAAC;AAAA,MACpE;AAEA,UAAI;AACF,gBAAQ,KAAK,MAAM,GAAG,CAAC;AAAA,MACzB,QAAQ;AACN,eAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,MACvC;AAAA,IACF,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,eAAsB,aACpB,OACA,QACA,QACA,KACA,KACA,YACA,WACA,WACe;AACf,MAAI;AAEF,QAAI,MAAe,CAAC;AACpB,QAAI,WAAW;AACb,YAAM,SAAS,MAAM,wBAAwB,KAAK,KAAK,YAAY,SAAS;AAC5E,UAAI,OAAO,QAAS;AACpB,YAAM,OAAO;AAAA,IACf;AAEA,UAAM,MAAM,MAAM,WAAW,MAAM,QAAQ;AAC3C,UAAM,cAAc,IAAI,MAAM;AAE9B,QAAI,CAAC,aAAa;AAChB,gBAAU,KAAK,sBAAsB,UAAU,MAAM,gBAAgB,KAAK,QAAW,SAAS;AAC9F;AAAA,IACF;AAEA,UAAM,UAAU,OAAO,gBAAgB,aAAa,cAAe,YAAwC;AAC3G,QAAI,OAAO,YAAY,YAAY;AACjC,gBAAU,KAAK,kBAAkB,mCAAmC,KAAK,QAAW,SAAS;AAC7F;AAAA,IACF;AAGA,UAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,QAAQ,WAAW,EAAE;AAC/E,UAAM,QAAgC,OAAO,YAAY,IAAI,YAAY;AAGzE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,UAAU,GAAG;AAAA,IAC5B,SAAS,KAAK;AACZ,gBAAU,KAAK,oBAAqB,IAAc,SAAS,KAAK,QAAW,SAAS;AACpF;AAAA,IACF;AAGA,UAAM,KAAK;AACX,QAAI,GAAG,SAAS,OAAQ,GAAG,MAAkC,cAAc,YAAY;AACrF,YAAM,SAAU,GAAG,MAAkC,UAAU,KAAK;AACpE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,4BAA4B,KAAK,OAAO,MAAM,QAAQ,SAAS;AAClG;AAAA,MACF;AACA,aAAO,OAAO,OAAO,OAAO,IAAI;AAAA,IAClC;AAEA,QAAI,GAAG,QAAQ,OAAQ,GAAG,KAAiC,cAAc,YAAY;AACnF,YAAM,SAAU,GAAG,KAAiC,UAAU,IAAI;AAClE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,wBAAwB,KAAK,OAAO,MAAM,QAAQ,SAAS;AAC9F;AAAA,MACF;AACA,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,GAAG,UAAU,OAAQ,GAAG,OAAmC,cAAc,YAAY;AACvF,YAAM,SAAU,GAAG,OAAmC,UAAU,MAAM;AACtE,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,KAAK,oBAAoB,4BAA4B,KAAK,OAAO,MAAM,QAAQ,SAAS;AAClG;AAAA,MACF;AACA,aAAO,OAAO,QAAQ,OAAO,IAAI;AAAA,IACnC;AAGA,UAAM,gBAAgB,MAAM,QAAQ,EAAE,OAAO,MAAM,QAAQ,SAAS,KAAK,IAAI,CAAC;AAG9E,QAAI,kBAAkB,UAAa,kBAAkB,MAAM;AACzD,eAAS,KAAK,MAAO,GAAG,UAAqB,GAAG;AAChD;AAAA,IACF;AAEA,QAAI,yBAAyB,UAAU;AACrC,UAAI,UAAU,cAAc,QAAQ,OAAO,YAAY,cAAc,OAAO,CAAC;AAE7E,UAAI,cAAc,MAAM;AACtB,cAAM,SAAS,cAAc,KAAK,UAAU;AAC5C,YAAI;AACF,iBAAO,MAAM;AACX,kBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AACV,gBAAI,MAAM,KAAK;AAAA,UACjB;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI,IAAI;AACR;AAAA,IACF;AAEA,aAAS,KAAK,eAAgB,GAAG,UAAqB,GAAG;AAAA,EAC3D,SAAS,KAAK;AACZ,QAAI,eAAe,mBAAmB;AACpC,gBAAU,KAAK,IAAI,MAAM,IAAI,SAAS,IAAI,QAAQ,QAAW,SAAS;AACtE;AAAA,IACF;AACA,cAAU,KAAK,kBAAmB,IAAc,WAAW,yBAAyB,KAAK,QAAW,SAAS;AAAA,EAC/G;AACF;;;AChMA,SAAS,qBAAqB;AAIvB,SAAS,iBAAiB,MAAiC;AAChE,SAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAC1C;AAEO,SAAS,yBAAqC;AACnD,SAAO,OAAO,SAAS;AACrB,UAAM,MAAM,cAAc,IAAI,EAAE;AAChC,WAAO,OAAO;AAAA,EAChB;AACF;;;ACFA,IAAM,gBAA0B,CAAC,QAAQ;AACvC,UAAQ,IAAI,KAAK,UAAU,GAAG,CAAC;AACjC;AAEO,SAAS,WACd,MACA,cACM;AACN,QAAM,MAAkB;AAAA,IACtB,OAAO;AAAA,IACP,GAAG;AAAA,IACH,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,QAAM,SAAS,gBAAgB;AAC/B,SAAO,GAAG;AACZ;;;ACVO,SAAS,kBAAkB,QAAyB;AACzD,QAAM,QAAQ,oBAAI,IAAwB;AAC1C,MAAI,aAAa;AAEjB,SAAO,SAAS,eAAe,KAAuC;AACpE,UAAM,MAAM,IAAI,QAAQ,iBAAiB;AACzC,UAAM,MAAM,KAAK,IAAI;AAGrB,QAAI,EAAE,aAAa,QAAS,GAAG;AAC7B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO;AAC1B,YAAI,EAAE,UAAU,IAAK,OAAM,OAAO,CAAC;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,IAAI,GAAG;AAE3B,QAAI,CAAC,SAAS,MAAM,MAAM,SAAS;AACjC,YAAM,IAAI,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,OAAO,SAAS,CAAC;AAC3D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,UACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,UACtC,yBAAyB,OAAO,OAAO,MAAM,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAEN,QAAI,MAAM,QAAQ,OAAO,KAAK;AAC5B,YAAM,aAAa,KAAK,MAAM,MAAM,UAAU,OAAO,GAAI;AACzD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,UACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,UACtC,yBAAyB;AAAA,UACzB,eAAe,OAAO,UAAU;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,QACP,qBAAqB,OAAO,OAAO,GAAG;AAAA,QACtC,yBAAyB,OAAO,OAAO,MAAM,MAAM,KAAK;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;;;ACjEO,SAAS,aACd,KACoD;AAEpD,MAAI,IAAI,QAAQ,eAAe,MAAM,KAAK;AACxC,WAAO,EAAE,OAAO,OAAO,QAAQ,+BAA+B;AAAA,EAChE;AAGA,QAAM,SAAS,IAAI,QAAQ,QAAQ;AACnC,MAAI,CAAC,QAAQ;AAEX,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,QAAM,OAAO,IAAI,QAAQ,MAAM;AAC/B,MAAI,CAAC,MAAM;AACT,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,MAAI;AACF,UAAM,aAAa,IAAI,IAAI,MAAM,EAAE;AACnC,QAAI,eAAe,MAAM;AACvB,aAAO,EAAE,OAAO,OAAO,QAAQ,UAAU,MAAM,wBAAwB,IAAI,GAAG;AAAA,IAChF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,OAAO,QAAQ,mBAAmB,MAAM,GAAG;AAAA,EAC7D;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;;;ACzBA,eAAsB,cACpB,UACA,YACA,KACA,KACA,YACA,WACA,WACe;AACf,MAAI;AAEF,UAAM,UAAU,IAAI,UAAU,OAAO,YAAY;AACjD,QAAI,WAAW,QAAQ;AACrB,gBAAU,KAAK,sBAAsB,4BAA4B,KAAK,QAAW,SAAS;AAC1F;AAAA,IACF;AAGA,UAAM,OAAO,aAAa,GAAG;AAC7B,QAAI,CAAC,KAAK,OAAO;AACf,gBAAU,KAAK,aAAa,KAAK,QAAQ,KAAK,QAAW,SAAS;AAClE;AAAA,IACF;AAGA,QAAI,MAAe,CAAC;AACpB,QAAI,WAAW;AACb,YAAMA,UAAS,MAAM,wBAAwB,KAAK,KAAK,YAAY,SAAS;AAC5E,UAAIA,QAAO,QAAS;AACpB,YAAMA,QAAO;AAAA,IACf;AAGA,UAAM,MAAM,MAAM,WAAW,QAAQ;AAGrC,UAAM,eAAe,IAAI,UAAU;AACnC,QAAI,CAAC,gBAAgB,OAAO,aAAa,YAAY,cAAc,CAAC,aAAa,OAAO;AACtF,gBAAU,KAAK,aAAa,WAAW,UAAU,eAAe,KAAK,QAAW,SAAS;AACzF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,UAAU,GAAG;AAAA,IAC5B,SAAS,KAAK;AACZ,gBAAU,KAAK,oBAAqB,IAAc,SAAS,KAAK,QAAW,SAAS;AACpF;AAAA,IACF;AAGA,UAAM,QAAQ,aAAa;AAC3B,UAAM,SAAS,MAAM,UAAU,IAAI;AACnC,QAAI,CAAC,OAAO,SAAS;AACnB,gBAAU,KAAK,oBAAoB,wBAAwB,KAAK,OAAO,OAAO,QAAQ,SAAS;AAC/F;AAAA,IACF;AAGA,UAAM,gBAAgB,MAAO,aAAa,QAAqB,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAG1F,QAAI,kBAAkB,UAAa,kBAAkB,MAAM;AACzD,eAAS,KAAK,MAAM,GAAG;AACvB;AAAA,IACF;AAEA,aAAS,KAAK,eAAe,GAAG;AAAA,EAClC,SAAS,KAAK;AACZ,QAAI,eAAe,mBAAmB;AACpC,gBAAU,KAAK,IAAI,MAAM,IAAI,SAAS,IAAI,QAAQ,QAAW,SAAS;AACtE;AAAA,IACF;AACA,cAAU,KAAK,kBAAmB,IAAc,WAAW,yBAAyB,KAAK,QAAW,SAAS;AAAA,EAC/G;AACF;","names":["result"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
theoPlugin
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZHZSF3AC.js";
|
|
5
6
|
|
|
6
7
|
// src/adapters/node.ts
|
|
7
8
|
import { build as viteBuild } from "vite";
|
|
@@ -40,4 +41,4 @@ var nodeAdapter = {
|
|
|
40
41
|
export {
|
|
41
42
|
nodeAdapter
|
|
42
43
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
44
|
+
//# sourceMappingURL=chunk-F2FOMARQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/node.ts"],"sourcesContent":["import { build as viteBuild } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { theoPlugin } from '../vite-plugin/index.js'\nimport type { DeployAdapter } from './types.js'\nimport type { TheoConfig } from '../config/schema.js'\n\nexport const nodeAdapter: DeployAdapter = {\n name: 'node',\n\n async build(config: TheoConfig, cwd: string): Promise<void> {\n // Client build\n await viteBuild({\n root: cwd,\n plugins: [react(), theoPlugin({ root: cwd, ssr: config.ssr })],\n build: {\n outDir: '.theo/client',\n emptyOutDir: true,\n },\n logLevel: 'info',\n })\n\n // SSR build (only when ssr: true)\n if (config.ssr) {\n console.log('\\n Building SSR...\\n')\n await viteBuild({\n root: cwd,\n plugins: [react(), theoPlugin({ root: cwd, ssr: true })],\n build: {\n ssr: true,\n outDir: '.theo/server',\n emptyOutDir: true,\n rollupOptions: {\n input: '/@theo/entry-server',\n },\n },\n logLevel: 'info',\n })\n }\n },\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/node.ts"],"sourcesContent":["import { build as viteBuild } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport { theoPlugin } from '../vite-plugin/index.js'\nimport type { DeployAdapter } from './types.js'\nimport type { TheoConfig } from '../config/schema.js'\n\nexport const nodeAdapter: DeployAdapter = {\n name: 'node',\n\n async build(config: TheoConfig, cwd: string): Promise<void> {\n // Client build\n await viteBuild({\n root: cwd,\n plugins: [react(), theoPlugin({ root: cwd, ssr: config.ssr })],\n build: {\n outDir: '.theo/client',\n emptyOutDir: true,\n },\n logLevel: 'info',\n })\n\n // SSR build (only when ssr: true)\n if (config.ssr) {\n console.log('\\n Building SSR...\\n')\n await viteBuild({\n root: cwd,\n plugins: [react(), theoPlugin({ root: cwd, ssr: true })],\n build: {\n ssr: true,\n outDir: '.theo/server',\n emptyOutDir: true,\n rollupOptions: {\n input: '/@theo/entry-server',\n },\n },\n logLevel: 'info',\n })\n }\n },\n}\n"],"mappings":";;;;;;;AAAA,SAAS,SAAS,iBAAiB;AACnC,OAAO,WAAW;AAKX,IAAM,cAA6B;AAAA,EACxC,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAA4B;AAE1D,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,SAAS,CAAC,MAAM,GAAG,WAAW,EAAE,MAAM,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC;AAAA,MAC7D,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAGD,QAAI,OAAO,KAAK;AACd,cAAQ,IAAI,uBAAuB;AACnC,YAAM,UAAU;AAAA,QACd,MAAM;AAAA,QACN,SAAS,CAAC,MAAM,GAAG,WAAW,EAAE,MAAM,KAAK,KAAK,KAAK,CAAC,CAAC;AAAA,QACvD,OAAO;AAAA,UACL,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/server/match.ts
|
|
4
5
|
function compilePattern(routePath) {
|
|
@@ -159,4 +160,4 @@ export {
|
|
|
159
160
|
scanServerActions,
|
|
160
161
|
scanWebSocketRoutes
|
|
161
162
|
};
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
163
|
+
//# sourceMappingURL=chunk-VXMNN5SA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/match.ts","../src/server/scan.ts","../src/server/action-scan.ts","../src/server/ws-scan.ts"],"sourcesContent":["export interface ServerRouteNode {\n filePath: string\n routePath: string\n paramNames: string[]\n pattern: RegExp\n}\n\nexport function compilePattern(routePath: string): {\n pattern: RegExp\n paramNames: string[]\n} {\n const paramNames: string[] = []\n const regexStr = routePath.replace(/:([^/]+)/g, (_, name) => {\n paramNames.push(name)\n return '([^/]+)'\n })\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","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname, basename } from 'node:path'\nimport { compilePattern, type ServerRouteNode } from './match.js'\n\nconst ROUTE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\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\n rel = rel.replace(/\\[([^\\]]+)\\]/g, ':$1')\n return `/api/${rel}`\n}\n\nfunction scanDir(dir: string, routesDir: string, results: ServerRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, routesDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ROUTE_EXTENSIONS.includes(ext)) continue\n\n const routePath = fileToRoutePath(fullPath, routesDir)\n const { pattern, paramNames } = compilePattern(routePath)\n results.push({\n filePath: resolve(fullPath),\n routePath,\n paramNames,\n pattern,\n })\n }\n }\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 scanDir(routesDir, routesDir, results)\n\n // Sort: static routes before dynamic (routes without params first)\n results.sort((a, b) => {\n if (a.paramNames.length === 0 && b.paramNames.length > 0) return -1\n if (a.paramNames.length > 0 && b.paramNames.length === 0) return 1\n return a.routePath.localeCompare(b.routePath)\n })\n\n return results\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nexport interface ActionNode {\n filePath: string\n actionPath: string\n}\n\nconst ACTION_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nfunction scanDir(dir: string, actionsDir: string, results: ActionNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, actionsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ACTION_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(actionsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n\n results.push({\n filePath: resolve(fullPath),\n actionPath: rel,\n })\n }\n }\n}\n\nexport function scanServerActions(serverDir: string): ActionNode[] {\n const actionsDir = join(serverDir, 'actions')\n if (!existsSync(actionsDir) || !statSync(actionsDir).isDirectory()) {\n return []\n }\n\n const results: ActionNode[] = []\n scanDir(actionsDir, actionsDir, results)\n return results\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nconst WS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nexport interface WebSocketRouteNode {\n filePath: string\n wsPath: string\n}\n\nfunction scanDir(dir: string, wsDir: string, results: WebSocketRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, wsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!WS_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(wsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n else if (rel === 'index') rel = ''\n\n results.push({\n filePath: resolve(fullPath),\n wsPath: `/ws/${rel}`,\n })\n }\n }\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 scanDir(wsDir, wsDir, results)\n return results\n}\n"],"mappings":"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","names":["readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir","readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/match.ts","../src/server/scan.ts","../src/server/action-scan.ts","../src/server/ws-scan.ts"],"sourcesContent":["export interface ServerRouteNode {\n filePath: string\n routePath: string\n paramNames: string[]\n pattern: RegExp\n}\n\nexport function compilePattern(routePath: string): {\n pattern: RegExp\n paramNames: string[]\n} {\n const paramNames: string[] = []\n const regexStr = routePath.replace(/:([^/]+)/g, (_, name) => {\n paramNames.push(name)\n return '([^/]+)'\n })\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","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname, basename } from 'node:path'\nimport { compilePattern, type ServerRouteNode } from './match.js'\n\nconst ROUTE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\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\n rel = rel.replace(/\\[([^\\]]+)\\]/g, ':$1')\n return `/api/${rel}`\n}\n\nfunction scanDir(dir: string, routesDir: string, results: ServerRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, routesDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ROUTE_EXTENSIONS.includes(ext)) continue\n\n const routePath = fileToRoutePath(fullPath, routesDir)\n const { pattern, paramNames } = compilePattern(routePath)\n results.push({\n filePath: resolve(fullPath),\n routePath,\n paramNames,\n pattern,\n })\n }\n }\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 scanDir(routesDir, routesDir, results)\n\n // Sort: static routes before dynamic (routes without params first)\n results.sort((a, b) => {\n if (a.paramNames.length === 0 && b.paramNames.length > 0) return -1\n if (a.paramNames.length > 0 && b.paramNames.length === 0) return 1\n return a.routePath.localeCompare(b.routePath)\n })\n\n return results\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nexport interface ActionNode {\n filePath: string\n actionPath: string\n}\n\nconst ACTION_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nfunction scanDir(dir: string, actionsDir: string, results: ActionNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, actionsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!ACTION_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(actionsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n\n results.push({\n filePath: resolve(fullPath),\n actionPath: rel,\n })\n }\n }\n}\n\nexport function scanServerActions(serverDir: string): ActionNode[] {\n const actionsDir = join(serverDir, 'actions')\n if (!existsSync(actionsDir) || !statSync(actionsDir).isDirectory()) {\n return []\n }\n\n const results: ActionNode[] = []\n scanDir(actionsDir, actionsDir, results)\n return results\n}\n","import { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, resolve, relative, extname } from 'node:path'\n\nconst WS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx']\n\nexport interface WebSocketRouteNode {\n filePath: string\n wsPath: string\n}\n\nfunction scanDir(dir: string, wsDir: string, results: WebSocketRouteNode[]): void {\n const entries = readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('_') && !entry.name.startsWith('.')) {\n scanDir(fullPath, wsDir, results)\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name)\n if (!WS_EXTENSIONS.includes(ext)) continue\n\n let rel = relative(wsDir, fullPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -ext.length)\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n else if (rel === 'index') rel = ''\n\n results.push({\n filePath: resolve(fullPath),\n wsPath: `/ws/${rel}`,\n })\n }\n }\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 scanDir(wsDir, wsDir, results)\n return results\n}\n"],"mappings":"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","names":["readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir","readdirSync","existsSync","statSync","join","resolve","relative","extname","scanDir"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
createRateLimiter,
|
|
4
5
|
createViteLoader,
|
|
@@ -6,13 +7,13 @@ import {
|
|
|
6
7
|
executeRoute,
|
|
7
8
|
logRequest,
|
|
8
9
|
sendError
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CDPRKHQ2.js";
|
|
10
11
|
import {
|
|
11
12
|
matchRoute,
|
|
12
13
|
scanServerActions,
|
|
13
14
|
scanServerRoutes,
|
|
14
15
|
scanWebSocketRoutes
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VXMNN5SA.js";
|
|
16
17
|
|
|
17
18
|
// src/vite-plugin/index.ts
|
|
18
19
|
import { resolve as resolve2, basename, dirname } from "path";
|
|
@@ -477,4 +478,4 @@ function theoPlugin(rootOrOptions) {
|
|
|
477
478
|
export {
|
|
478
479
|
theoPlugin
|
|
479
480
|
};
|
|
480
|
-
//# sourceMappingURL=chunk-
|
|
481
|
+
//# sourceMappingURL=chunk-ZHZSF3AC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin/index.ts","../src/router/scan.ts","../src/router/types.ts","../src/router/generate.ts","../src/router/entry.ts","../src/router/entry-server.ts","../src/vite-plugin/api-middleware.ts","../src/vite-plugin/action-middleware.ts"],"sourcesContent":["import type { Plugin } from 'vite'\nimport { resolve, basename, dirname } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { scanRoutes } from '../router/scan.js'\nimport { generateRouteManifest } from '../router/generate.js'\nimport { generateEntryClient } from '../router/entry.js'\nimport { generateEntryServer } from '../router/entry-server.js'\nimport { isRouteFile } from '../router/types.js'\nimport { createApiMiddleware } from './api-middleware.js'\nimport { createActionMiddleware } from './action-middleware.js'\nimport { scanWebSocketRoutes } from '../server/ws-scan.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nconst VIRTUAL_ENTRY_ID = '/@theo/entry-client'\nconst RESOLVED_ENTRY_ID = '\\0@theo/entry-client'\nconst VIRTUAL_MANIFEST_ID = '/@theo/route-manifest'\nconst RESOLVED_MANIFEST_ID = '\\0@theo/route-manifest'\nconst VIRTUAL_ENTRY_SERVER_ID = '/@theo/entry-server'\nconst RESOLVED_ENTRY_SERVER_ID = '\\0@theo/entry-server'\n\nexport interface TheoPluginOptions {\n root?: string\n rateLimit?: RateLimitConfig\n ssr?: boolean\n}\n\nexport function theoPlugin(rootOrOptions?: string | TheoPluginOptions): Plugin {\n const options = typeof rootOrOptions === 'string' ? { root: rootOrOptions } : (rootOrOptions ?? {})\n const projectRoot = options.root ?? process.cwd()\n const appDir = resolve(projectRoot, 'app')\n const ssrEnabled = options.ssr ?? false\n\n // Resolve paths for SSR module loading\n const currentDir = dirname(fileURLToPath(import.meta.url))\n const theoSrcDir = resolve(currentDir, '..')\n\n return {\n name: 'theo',\n\n config() {\n // Detect whether we're running from source (.ts) or compiled dist (.js)\n const ext = existsSync(resolve(theoSrcDir, 'index.ts')) ? '.ts' : '.js'\n return {\n envPrefix: 'THEO_PUBLIC_',\n resolve: {\n alias: [\n { find: 'theokit/server', replacement: resolve(theoSrcDir, `server/index${ext}`) },\n { find: 'theokit', replacement: resolve(theoSrcDir, `index${ext}`) },\n ],\n },\n }\n },\n\n resolveId(id: string) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID\n if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_MANIFEST_ID\n if (id === VIRTUAL_ENTRY_SERVER_ID) return RESOLVED_ENTRY_SERVER_ID\n },\n\n load(id: string) {\n if (id === RESOLVED_ENTRY_ID) {\n return generateEntryClient(ssrEnabled)\n }\n if (id === RESOLVED_MANIFEST_ID) {\n const tree = scanRoutes(appDir)\n return generateRouteManifest(tree)\n }\n if (id === RESOLVED_ENTRY_SERVER_ID) {\n return generateEntryServer()\n }\n },\n\n configureServer(server) {\n // Server middleware (action before API — more specific prefix first)\n const serverDir = resolve(projectRoot, 'server')\n server.middlewares.use(createActionMiddleware(server, serverDir))\n server.middlewares.use(createApiMiddleware(server, serverDir, options.rateLimit))\n\n // SSR dev middleware\n if (ssrEnabled) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? '/'\n // Skip API, static, and HMR requests\n if (url.startsWith('/api/') || url.startsWith('/@') || url.startsWith('/node_modules/') || url.includes('.')) {\n return next()\n }\n\n try {\n const indexPath = resolve(projectRoot, 'index.html')\n let template = readFileSync(indexPath, 'utf-8')\n template = await server.transformIndexHtml(url, template)\n\n const mod = await server.ssrLoadModule(VIRTUAL_ENTRY_SERVER_ID)\n const result = await mod.render(url)\n\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: (result.redirect as Response).headers.get('location') ?? '/' })\n res.end()\n return\n }\n\n // render() returns HTML string — inject into template\n const ssrHtml = result as string\n const rootDivMatch = template.match(/<div id=[\"']root[\"'][^>]*>/)\n if (!rootDivMatch) {\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(template)\n return\n }\n\n const splitIdx = template.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n const html = template.slice(0, splitIdx) + ssrHtml + template.slice(splitIdx)\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(html)\n } catch (err) {\n server.ssrFixStacktrace(err as Error)\n console.error('[SSR Dev Error]', err)\n // Fallback to CSR\n return next()\n }\n })\n }\n\n // Frontend HMR watcher\n function handleRouteChange(filePath: string) {\n if (!isRouteFile(basename(filePath))) return\n if (!filePath.startsWith(appDir)) return\n\n const mod = server.moduleGraph.getModuleById(RESOLVED_MANIFEST_ID)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n server.ws.send({ type: 'full-reload' })\n }\n }\n\n server.watcher.on('add', handleRouteChange)\n server.watcher.on('unlink', handleRouteChange)\n\n // WebSocket upgrade handler (dev mode)\n const wsRoutes = scanWebSocketRoutes(resolve(projectRoot, 'server'))\n if (wsRoutes.length > 0 && server.httpServer) {\n import('ws').then(({ WebSocketServer }) => {\n const wss = new WebSocketServer({ noServer: true })\n\n server.httpServer!.on('upgrade', async (request, socket, head) => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) return // Let Vite handle HMR etc.\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find(r => r.wsPath === wsPath)\n if (!match) { socket.destroy(); return }\n\n try {\n const mod = await server.ssrLoadModule(match.filePath)\n const handler = mod.default ?? mod\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => handler.onMessage?.(ws, data.toString()))\n ws.on('close', (code: number, reason: Buffer) => handler.onClose?.(ws, code, reason))\n ws.on('error', (err: Error) => handler.onError?.(ws, err))\n })\n } catch {\n socket.destroy()\n }\n })\n }).catch(() => {\n console.warn('[Theo] WebSocket routes found but \"ws\" package not installed. Run: npm install ws')\n })\n }\n },\n }\n}\n","import { readdirSync, statSync, existsSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport type { RouteNode, RouteFileName } from './types.js'\nimport { ROUTE_FILE_NAMES, ROUTE_FILE_EXTENSIONS } from './types.js'\n\nfunction toNodeKey(name: RouteFileName): 'page' | 'layout' | 'error' | 'loading' | 'notFound' {\n if (name === 'not-found') return 'notFound'\n return name as 'page' | 'layout' | 'error' | 'loading'\n}\n\nfunction setRouteFile(node: RouteNode, key: 'page' | 'layout' | 'error' | 'loading' | 'notFound', value: string): void {\n switch (key) {\n case 'page': node.page = value; break\n case 'layout': node.layout = value; break\n case 'error': node.error = value; break\n case 'loading': node.loading = value; break\n case 'notFound': node.notFound = value; break\n }\n}\n\nfunction scanDir(dir: string, segment: string, routePath: string): RouteNode {\n const node: RouteNode = { segment, path: routePath, children: [] }\n\n const entries = readdirSync(dir, { withFileTypes: true })\n\n // Check route files with extension priority (.tsx > .ts > .jsx > .js)\n for (const name of ROUTE_FILE_NAMES) {\n const key = toNodeKey(name)\n if (node[key] !== undefined) continue\n for (const ext of ROUTE_FILE_EXTENSIONS) {\n const filename = `${name}${ext}`\n if (existsSync(join(dir, filename))) {\n setRouteFile(node, key, resolve(dir, filename))\n break\n }\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue\n\n const childPath =\n routePath === '/' ? `/${entry.name}` : `${routePath}/${entry.name}`\n const child = scanDir(join(dir, entry.name), entry.name, childPath)\n\n // Prune empty nodes\n const hasRouteFile =\n child.page ||\n child.layout ||\n child.error ||\n child.loading ||\n child.notFound\n if (hasRouteFile || child.children.length > 0) {\n node.children.push(child)\n }\n }\n\n return node\n}\n\nexport function scanRoutes(appDir: string): RouteNode {\n if (!existsSync(appDir)) {\n throw new Error(`App directory does not exist: ${appDir}`)\n }\n if (!statSync(appDir).isDirectory()) {\n throw new Error(`App path is not a directory: ${appDir}`)\n }\n return scanDir(appDir, '', '/')\n}\n","export interface RouteNode {\n segment: string\n path: string\n page?: string\n layout?: string\n error?: string\n loading?: string\n notFound?: string\n children: RouteNode[]\n}\n\nexport const ROUTE_FILE_NAMES = [\n 'page',\n 'layout',\n 'error',\n 'loading',\n 'not-found',\n] as const\n\nexport type RouteFileName = (typeof ROUTE_FILE_NAMES)[number]\n\nexport const ROUTE_FILE_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'] as const\n\nconst ROUTE_FILE_REGEX =\n /^(page|layout|error|loading|not-found)\\.(tsx|ts|jsx|js)$/\n\nexport function isRouteFile(filename: string): boolean {\n return ROUTE_FILE_REGEX.test(filename)\n}\n","import type { RouteNode } from './types.js'\n\nfunction normalizePath(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\nfunction safeVarName(segment: string, prefix: string): string {\n const safe = segment.replace(/[^a-zA-Z0-9]/g, '_') || 'root'\n return `${prefix}_${safe}`\n}\n\ninterface ImportEntry {\n varName: string\n importPath: string\n}\n\nexport function generateRouteManifest(tree: RouteNode): string {\n const imports: ImportEntry[] = []\n let hasLayout = false\n\n function collectImports(node: RouteNode): void {\n const seg = node.segment || 'root'\n if (node.page) {\n imports.push({\n varName: safeVarName(seg, 'Page'),\n importPath: normalizePath(node.page),\n })\n }\n if (node.layout) {\n hasLayout = true\n imports.push({\n varName: safeVarName(seg, 'Layout'),\n importPath: normalizePath(node.layout),\n })\n }\n if (node.error) {\n imports.push({\n varName: safeVarName(seg, 'Error'),\n importPath: normalizePath(node.error),\n })\n }\n if (node.loading) {\n imports.push({\n varName: safeVarName(seg, 'Loading'),\n importPath: normalizePath(node.loading),\n })\n }\n if (node.notFound) {\n imports.push({\n varName: safeVarName(seg, 'NotFound'),\n importPath: normalizePath(node.notFound),\n })\n }\n for (const child of node.children) {\n collectImports(child)\n }\n }\n\n collectImports(tree)\n\n // Build import lines\n const lines: string[] = [\n `import React, { Suspense, lazy } from 'react'`,\n ]\n\n if (hasLayout) {\n lines.push(`import { Outlet } from 'react-router'`)\n }\n\n lines.push('')\n\n for (const imp of imports) {\n lines.push(\n `const ${imp.varName} = lazy(() => import('${imp.importPath}'))`,\n )\n }\n\n lines.push('')\n\n // Generate route config\n function genRouteConfig(node: RouteNode, isRoot: boolean): string {\n const seg = node.segment || 'root'\n const childConfigs: string[] = []\n\n // Index route for this node's page\n if (node.page) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n childConfigs.push(`{ index: true, element: ${pageElement} }`)\n }\n\n // Child routes\n for (const child of node.children) {\n childConfigs.push(genRouteConfig(child, false))\n }\n\n // Not-found wildcard (only at this level)\n if (node.notFound) {\n const nfVar = safeVarName(seg, 'NotFound')\n childConfigs.push(\n `{ path: '*', element: React.createElement(${nfVar}) }`,\n )\n }\n\n // Wrap children in error boundary (pathless wrapper) if error exists\n let childrenArray = `[${childConfigs.join(', ')}]`\n if (node.error) {\n const errVar = safeVarName(seg, 'Error')\n childrenArray = `[{ errorElement: React.createElement(${errVar}), children: ${childrenArray} }]`\n }\n\n // Build route object\n if (node.layout) {\n const layoutVar = safeVarName(seg, 'Layout')\n const pathPart = isRoot\n ? `path: '/'`\n : `path: '${node.segment}'`\n return `{ ${pathPart}, element: React.createElement(${layoutVar}), children: ${childrenArray} }`\n }\n\n // No layout — if root, wrap in path '/'\n if (isRoot) {\n if (childConfigs.length === 0 && !node.page) {\n return `{ path: '/', children: [] }`\n }\n // Root without layout: children are direct routes\n return `{ path: '/', children: ${childrenArray} }`\n }\n\n // Child segment without layout — just a route\n if (node.page && node.children.length === 0 && !node.error && !node.notFound) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n return `{ path: '${node.segment}', element: ${pageElement} }`\n }\n\n // Child with children but no layout\n return `{ path: '${node.segment}', children: ${childrenArray} }`\n }\n\n const routeConfig = genRouteConfig(tree, true)\n lines.push(`export const routes = [${routeConfig}]`)\n\n return lines.join('\\n')\n}\n","export function generateEntryClient(ssr?: boolean): string {\n const rootMethod = ssr ? 'hydrateRoot' : 'createRoot'\n const renderCall = ssr\n ? ` ${rootMethod}(el,\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n : ` ${rootMethod}(el).render(\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n\n return [\n `import React, { Suspense } from 'react'`,\n `import { ${rootMethod} } from 'react-dom/client'`,\n `import { createBrowserRouter, RouterProvider } from 'react-router'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `const router = createBrowserRouter(routes)`,\n `const el = document.getElementById('root')`,\n `if (el) {`,\n renderCall,\n `}`,\n ].join('\\n')\n}\n","export function generateEntryServer(): string {\n return [\n `import React from 'react'`,\n `import { renderToPipeableStream } from 'react-dom/server'`,\n `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,\n `import { PassThrough } from 'node:stream'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `export async function render(url) {`,\n ` const handler = createStaticHandler(routes)`,\n ` const request = new Request('http://localhost' + url)`,\n ` const context = await handler.query(request)`,\n ``,\n ` if (context instanceof Response) {`,\n ` return { redirect: context }`,\n ` }`,\n ``,\n ` const router = createStaticRouter(handler.dataRoutes, context)`,\n ` const app = React.createElement(StaticRouterProvider, { router, context })`,\n ``,\n ` return new Promise((resolve, reject) => {`,\n ` let html = ''`,\n ` const passthrough = new PassThrough()`,\n ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,\n ` passthrough.on('end', () => { resolve(html) })`,\n ` passthrough.on('error', reject)`,\n ``,\n ` const { pipe } = renderToPipeableStream(app, {`,\n ` onAllReady() { pipe(passthrough) },`,\n ` onShellError(err) { reject(err) },`,\n ` onError(err) { console.error('[SSR Error]', err) },`,\n ` })`,\n ` })`,\n `}`,\n ].join('\\n')\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerRoutes } from '../server/scan.js'\nimport { matchRoute } from '../server/match.js'\nimport { executeRoute, sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\nimport { createRateLimiter } from '../server/rate-limit.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nexport function createApiMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n rateLimitConfig?: RateLimitConfig,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n const rateLimiter = rateLimitConfig ? createRateLimiter(rateLimitConfig) : null\n\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith('/api/')) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const routes = scanServerRoutes(serverDir)\n const match = matchRoute(url, routes)\n\n if (!match) {\n sendError(res, 'NOT_FOUND', 'API route not found', 404, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n const method = (req.method ?? 'GET').toUpperCase()\n await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir, requestId)\n logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerActions } from '../server/action-scan.js'\nimport { executeAction } from '../server/action-execute.js'\nimport { sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\n\nconst PREFIX = '/api/__actions/'\n\nexport function createActionMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith(PREFIX)) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n const pathAfterPrefix = url.slice(PREFIX.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n\n if (segments.length < 2) {\n sendError(res, 'BAD_REQUEST', 'Action URL must be /api/__actions/{file}/{exportName}', 400, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 400, duration: Date.now() - start, requestId })\n return\n }\n\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n\n const actions = scanServerActions(serverDir)\n const action = actions.find((a) => a.actionPath === actionPath)\n\n if (!action) {\n sendError(res, 'NOT_FOUND', `Action file \"${actionPath}\" not found`, 404, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n await executeAction(action.filePath, exportName, req, res, loadModule, serverDir, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n"],"mappings":"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","names":["resolve","existsSync","randomUUID","randomUUID","resolve","existsSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/index.ts","../src/router/scan.ts","../src/router/types.ts","../src/router/generate.ts","../src/router/entry.ts","../src/router/entry-server.ts","../src/vite-plugin/api-middleware.ts","../src/vite-plugin/action-middleware.ts"],"sourcesContent":["import type { Plugin } from 'vite'\nimport { resolve, basename, dirname } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { scanRoutes } from '../router/scan.js'\nimport { generateRouteManifest } from '../router/generate.js'\nimport { generateEntryClient } from '../router/entry.js'\nimport { generateEntryServer } from '../router/entry-server.js'\nimport { isRouteFile } from '../router/types.js'\nimport { createApiMiddleware } from './api-middleware.js'\nimport { createActionMiddleware } from './action-middleware.js'\nimport { scanWebSocketRoutes } from '../server/ws-scan.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nconst VIRTUAL_ENTRY_ID = '/@theo/entry-client'\nconst RESOLVED_ENTRY_ID = '\\0@theo/entry-client'\nconst VIRTUAL_MANIFEST_ID = '/@theo/route-manifest'\nconst RESOLVED_MANIFEST_ID = '\\0@theo/route-manifest'\nconst VIRTUAL_ENTRY_SERVER_ID = '/@theo/entry-server'\nconst RESOLVED_ENTRY_SERVER_ID = '\\0@theo/entry-server'\n\nexport interface TheoPluginOptions {\n root?: string\n rateLimit?: RateLimitConfig\n ssr?: boolean\n}\n\nexport function theoPlugin(rootOrOptions?: string | TheoPluginOptions): Plugin {\n const options = typeof rootOrOptions === 'string' ? { root: rootOrOptions } : (rootOrOptions ?? {})\n const projectRoot = options.root ?? process.cwd()\n const appDir = resolve(projectRoot, 'app')\n const ssrEnabled = options.ssr ?? false\n\n // Resolve paths for SSR module loading\n const currentDir = dirname(fileURLToPath(import.meta.url))\n const theoSrcDir = resolve(currentDir, '..')\n\n return {\n name: 'theo',\n\n config() {\n // Detect whether we're running from source (.ts) or compiled dist (.js)\n const ext = existsSync(resolve(theoSrcDir, 'index.ts')) ? '.ts' : '.js'\n return {\n envPrefix: 'THEO_PUBLIC_',\n resolve: {\n alias: [\n { find: 'theokit/server', replacement: resolve(theoSrcDir, `server/index${ext}`) },\n { find: 'theokit', replacement: resolve(theoSrcDir, `index${ext}`) },\n ],\n },\n }\n },\n\n resolveId(id: string) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID\n if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_MANIFEST_ID\n if (id === VIRTUAL_ENTRY_SERVER_ID) return RESOLVED_ENTRY_SERVER_ID\n },\n\n load(id: string) {\n if (id === RESOLVED_ENTRY_ID) {\n return generateEntryClient(ssrEnabled)\n }\n if (id === RESOLVED_MANIFEST_ID) {\n const tree = scanRoutes(appDir)\n return generateRouteManifest(tree)\n }\n if (id === RESOLVED_ENTRY_SERVER_ID) {\n return generateEntryServer()\n }\n },\n\n configureServer(server) {\n // Server middleware (action before API — more specific prefix first)\n const serverDir = resolve(projectRoot, 'server')\n server.middlewares.use(createActionMiddleware(server, serverDir))\n server.middlewares.use(createApiMiddleware(server, serverDir, options.rateLimit))\n\n // SSR dev middleware\n if (ssrEnabled) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url ?? '/'\n // Skip API, static, and HMR requests\n if (url.startsWith('/api/') || url.startsWith('/@') || url.startsWith('/node_modules/') || url.includes('.')) {\n return next()\n }\n\n try {\n const indexPath = resolve(projectRoot, 'index.html')\n let template = readFileSync(indexPath, 'utf-8')\n template = await server.transformIndexHtml(url, template)\n\n const mod = await server.ssrLoadModule(VIRTUAL_ENTRY_SERVER_ID)\n const result = await mod.render(url)\n\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: (result.redirect as Response).headers.get('location') ?? '/' })\n res.end()\n return\n }\n\n // render() returns HTML string — inject into template\n const ssrHtml = result as string\n const rootDivMatch = template.match(/<div id=[\"']root[\"'][^>]*>/)\n if (!rootDivMatch) {\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(template)\n return\n }\n\n const splitIdx = template.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n const html = template.slice(0, splitIdx) + ssrHtml + template.slice(splitIdx)\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(html)\n } catch (err) {\n server.ssrFixStacktrace(err as Error)\n console.error('[SSR Dev Error]', err)\n // Fallback to CSR\n return next()\n }\n })\n }\n\n // Frontend HMR watcher\n function handleRouteChange(filePath: string) {\n if (!isRouteFile(basename(filePath))) return\n if (!filePath.startsWith(appDir)) return\n\n const mod = server.moduleGraph.getModuleById(RESOLVED_MANIFEST_ID)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n server.ws.send({ type: 'full-reload' })\n }\n }\n\n server.watcher.on('add', handleRouteChange)\n server.watcher.on('unlink', handleRouteChange)\n\n // WebSocket upgrade handler (dev mode)\n const wsRoutes = scanWebSocketRoutes(resolve(projectRoot, 'server'))\n if (wsRoutes.length > 0 && server.httpServer) {\n import('ws').then(({ WebSocketServer }) => {\n const wss = new WebSocketServer({ noServer: true })\n\n server.httpServer!.on('upgrade', async (request, socket, head) => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) return // Let Vite handle HMR etc.\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find(r => r.wsPath === wsPath)\n if (!match) { socket.destroy(); return }\n\n try {\n const mod = await server.ssrLoadModule(match.filePath)\n const handler = mod.default ?? mod\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => handler.onMessage?.(ws, data.toString()))\n ws.on('close', (code: number, reason: Buffer) => handler.onClose?.(ws, code, reason))\n ws.on('error', (err: Error) => handler.onError?.(ws, err))\n })\n } catch {\n socket.destroy()\n }\n })\n }).catch(() => {\n console.warn('[Theo] WebSocket routes found but \"ws\" package not installed. Run: npm install ws')\n })\n }\n },\n }\n}\n","import { readdirSync, statSync, existsSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport type { RouteNode, RouteFileName } from './types.js'\nimport { ROUTE_FILE_NAMES, ROUTE_FILE_EXTENSIONS } from './types.js'\n\nfunction toNodeKey(name: RouteFileName): 'page' | 'layout' | 'error' | 'loading' | 'notFound' {\n if (name === 'not-found') return 'notFound'\n return name as 'page' | 'layout' | 'error' | 'loading'\n}\n\nfunction setRouteFile(node: RouteNode, key: 'page' | 'layout' | 'error' | 'loading' | 'notFound', value: string): void {\n switch (key) {\n case 'page': node.page = value; break\n case 'layout': node.layout = value; break\n case 'error': node.error = value; break\n case 'loading': node.loading = value; break\n case 'notFound': node.notFound = value; break\n }\n}\n\nfunction scanDir(dir: string, segment: string, routePath: string): RouteNode {\n const node: RouteNode = { segment, path: routePath, children: [] }\n\n const entries = readdirSync(dir, { withFileTypes: true })\n\n // Check route files with extension priority (.tsx > .ts > .jsx > .js)\n for (const name of ROUTE_FILE_NAMES) {\n const key = toNodeKey(name)\n if (node[key] !== undefined) continue\n for (const ext of ROUTE_FILE_EXTENSIONS) {\n const filename = `${name}${ext}`\n if (existsSync(join(dir, filename))) {\n setRouteFile(node, key, resolve(dir, filename))\n break\n }\n }\n }\n\n // Recurse into subdirectories\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue\n\n const childPath =\n routePath === '/' ? `/${entry.name}` : `${routePath}/${entry.name}`\n const child = scanDir(join(dir, entry.name), entry.name, childPath)\n\n // Prune empty nodes\n const hasRouteFile =\n child.page ||\n child.layout ||\n child.error ||\n child.loading ||\n child.notFound\n if (hasRouteFile || child.children.length > 0) {\n node.children.push(child)\n }\n }\n\n return node\n}\n\nexport function scanRoutes(appDir: string): RouteNode {\n if (!existsSync(appDir)) {\n throw new Error(`App directory does not exist: ${appDir}`)\n }\n if (!statSync(appDir).isDirectory()) {\n throw new Error(`App path is not a directory: ${appDir}`)\n }\n return scanDir(appDir, '', '/')\n}\n","export interface RouteNode {\n segment: string\n path: string\n page?: string\n layout?: string\n error?: string\n loading?: string\n notFound?: string\n children: RouteNode[]\n}\n\nexport const ROUTE_FILE_NAMES = [\n 'page',\n 'layout',\n 'error',\n 'loading',\n 'not-found',\n] as const\n\nexport type RouteFileName = (typeof ROUTE_FILE_NAMES)[number]\n\nexport const ROUTE_FILE_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'] as const\n\nconst ROUTE_FILE_REGEX =\n /^(page|layout|error|loading|not-found)\\.(tsx|ts|jsx|js)$/\n\nexport function isRouteFile(filename: string): boolean {\n return ROUTE_FILE_REGEX.test(filename)\n}\n","import type { RouteNode } from './types.js'\n\nfunction normalizePath(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\nfunction safeVarName(segment: string, prefix: string): string {\n const safe = segment.replace(/[^a-zA-Z0-9]/g, '_') || 'root'\n return `${prefix}_${safe}`\n}\n\ninterface ImportEntry {\n varName: string\n importPath: string\n}\n\nexport function generateRouteManifest(tree: RouteNode): string {\n const imports: ImportEntry[] = []\n let hasLayout = false\n\n function collectImports(node: RouteNode): void {\n const seg = node.segment || 'root'\n if (node.page) {\n imports.push({\n varName: safeVarName(seg, 'Page'),\n importPath: normalizePath(node.page),\n })\n }\n if (node.layout) {\n hasLayout = true\n imports.push({\n varName: safeVarName(seg, 'Layout'),\n importPath: normalizePath(node.layout),\n })\n }\n if (node.error) {\n imports.push({\n varName: safeVarName(seg, 'Error'),\n importPath: normalizePath(node.error),\n })\n }\n if (node.loading) {\n imports.push({\n varName: safeVarName(seg, 'Loading'),\n importPath: normalizePath(node.loading),\n })\n }\n if (node.notFound) {\n imports.push({\n varName: safeVarName(seg, 'NotFound'),\n importPath: normalizePath(node.notFound),\n })\n }\n for (const child of node.children) {\n collectImports(child)\n }\n }\n\n collectImports(tree)\n\n // Build import lines\n const lines: string[] = [\n `import React, { Suspense, lazy } from 'react'`,\n ]\n\n if (hasLayout) {\n lines.push(`import { Outlet } from 'react-router'`)\n }\n\n lines.push('')\n\n for (const imp of imports) {\n lines.push(\n `const ${imp.varName} = lazy(() => import('${imp.importPath}'))`,\n )\n }\n\n lines.push('')\n\n // Generate route config\n function genRouteConfig(node: RouteNode, isRoot: boolean): string {\n const seg = node.segment || 'root'\n const childConfigs: string[] = []\n\n // Index route for this node's page\n if (node.page) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n childConfigs.push(`{ index: true, element: ${pageElement} }`)\n }\n\n // Child routes\n for (const child of node.children) {\n childConfigs.push(genRouteConfig(child, false))\n }\n\n // Not-found wildcard (only at this level)\n if (node.notFound) {\n const nfVar = safeVarName(seg, 'NotFound')\n childConfigs.push(\n `{ path: '*', element: React.createElement(${nfVar}) }`,\n )\n }\n\n // Wrap children in error boundary (pathless wrapper) if error exists\n let childrenArray = `[${childConfigs.join(', ')}]`\n if (node.error) {\n const errVar = safeVarName(seg, 'Error')\n childrenArray = `[{ errorElement: React.createElement(${errVar}), children: ${childrenArray} }]`\n }\n\n // Build route object\n if (node.layout) {\n const layoutVar = safeVarName(seg, 'Layout')\n const pathPart = isRoot\n ? `path: '/'`\n : `path: '${node.segment}'`\n return `{ ${pathPart}, element: React.createElement(${layoutVar}), children: ${childrenArray} }`\n }\n\n // No layout — if root, wrap in path '/'\n if (isRoot) {\n if (childConfigs.length === 0 && !node.page) {\n return `{ path: '/', children: [] }`\n }\n // Root without layout: children are direct routes\n return `{ path: '/', children: ${childrenArray} }`\n }\n\n // Child segment without layout — just a route\n if (node.page && node.children.length === 0 && !node.error && !node.notFound) {\n const pageVar = safeVarName(seg, 'Page')\n let pageElement = `React.createElement(${pageVar})`\n if (node.loading) {\n const loadVar = safeVarName(seg, 'Loading')\n pageElement = `React.createElement(Suspense, { fallback: React.createElement(${loadVar}) }, ${pageElement})`\n }\n return `{ path: '${node.segment}', element: ${pageElement} }`\n }\n\n // Child with children but no layout\n return `{ path: '${node.segment}', children: ${childrenArray} }`\n }\n\n const routeConfig = genRouteConfig(tree, true)\n lines.push(`export const routes = [${routeConfig}]`)\n\n return lines.join('\\n')\n}\n","export function generateEntryClient(ssr?: boolean): string {\n const rootMethod = ssr ? 'hydrateRoot' : 'createRoot'\n const renderCall = ssr\n ? ` ${rootMethod}(el,\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n : ` ${rootMethod}(el).render(\\n React.createElement(Suspense, { fallback: null },\\n React.createElement(RouterProvider, { router })\\n )\\n )`\n\n return [\n `import React, { Suspense } from 'react'`,\n `import { ${rootMethod} } from 'react-dom/client'`,\n `import { createBrowserRouter, RouterProvider } from 'react-router'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `const router = createBrowserRouter(routes)`,\n `const el = document.getElementById('root')`,\n `if (el) {`,\n renderCall,\n `}`,\n ].join('\\n')\n}\n","export function generateEntryServer(): string {\n return [\n `import React from 'react'`,\n `import { renderToPipeableStream } from 'react-dom/server'`,\n `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,\n `import { PassThrough } from 'node:stream'`,\n `import { routes } from '/@theo/route-manifest'`,\n ``,\n `export async function render(url) {`,\n ` const handler = createStaticHandler(routes)`,\n ` const request = new Request('http://localhost' + url)`,\n ` const context = await handler.query(request)`,\n ``,\n ` if (context instanceof Response) {`,\n ` return { redirect: context }`,\n ` }`,\n ``,\n ` const router = createStaticRouter(handler.dataRoutes, context)`,\n ` const app = React.createElement(StaticRouterProvider, { router, context })`,\n ``,\n ` return new Promise((resolve, reject) => {`,\n ` let html = ''`,\n ` const passthrough = new PassThrough()`,\n ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,\n ` passthrough.on('end', () => { resolve(html) })`,\n ` passthrough.on('error', reject)`,\n ``,\n ` const { pipe } = renderToPipeableStream(app, {`,\n ` onAllReady() { pipe(passthrough) },`,\n ` onShellError(err) { reject(err) },`,\n ` onError(err) { console.error('[SSR Error]', err) },`,\n ` })`,\n ` })`,\n `}`,\n ].join('\\n')\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerRoutes } from '../server/scan.js'\nimport { matchRoute } from '../server/match.js'\nimport { executeRoute, sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\nimport { createRateLimiter } from '../server/rate-limit.js'\nimport type { RateLimitConfig } from '../server/rate-limit.js'\n\nexport function createApiMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n rateLimitConfig?: RateLimitConfig,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n const rateLimiter = rateLimitConfig ? createRateLimiter(rateLimitConfig) : null\n\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith('/api/')) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const routes = scanServerRoutes(serverDir)\n const match = matchRoute(url, routes)\n\n if (!match) {\n sendError(res, 'NOT_FOUND', 'API route not found', 404, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n const method = (req.method ?? 'GET').toUpperCase()\n await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir, requestId)\n logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n","import type { ViteDevServer, Connect } from 'vite'\nimport { randomUUID } from 'node:crypto'\nimport { scanServerActions } from '../server/action-scan.js'\nimport { executeAction } from '../server/action-execute.js'\nimport { sendError } from '../server/execute.js'\nimport { createViteLoader } from '../server/module-loader.js'\nimport { logRequest } from '../server/logger.js'\n\nconst PREFIX = '/api/__actions/'\n\nexport function createActionMiddleware(\n vite: ViteDevServer,\n serverDir: string,\n): Connect.NextHandleFunction {\n const loadModule = createViteLoader(vite)\n return async (req, res, next) => {\n const url = req.url ?? ''\n if (!url.startsWith(PREFIX)) {\n return next()\n }\n\n const requestId = randomUUID()\n const start = Date.now()\n res.setHeader('x-request-id', requestId)\n\n const pathAfterPrefix = url.slice(PREFIX.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n\n if (segments.length < 2) {\n sendError(res, 'BAD_REQUEST', 'Action URL must be /api/__actions/{file}/{exportName}', 400, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 400, duration: Date.now() - start, requestId })\n return\n }\n\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n\n const actions = scanServerActions(serverDir)\n const action = actions.find((a) => a.actionPath === actionPath)\n\n if (!action) {\n sendError(res, 'NOT_FOUND', `Action file \"${actionPath}\" not found`, 404, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n\n await executeAction(action.filePath, exportName, req, res, loadModule, serverDir, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: res.statusCode, duration: Date.now() - start, requestId })\n }\n}\n"],"mappings":"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","names":["resolve","existsSync","randomUUID","randomUUID","resolve","existsSync"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/cli/index.ts
|
|
4
5
|
import cac from "cac";
|
|
5
6
|
var cli = cac("theokit");
|
|
6
7
|
cli.command("dev", "Start development server").option("--port <port>", "Port number").action(async (options) => {
|
|
7
|
-
const { devCommand } = await import("../dev-
|
|
8
|
+
const { devCommand } = await import("../dev-E3LMBL7C.js");
|
|
8
9
|
await devCommand({ port: options.port ? Number(options.port) : void 0 });
|
|
9
10
|
});
|
|
10
11
|
cli.command("build", "Build for production").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
|
|
11
12
|
try {
|
|
12
|
-
const { buildCommand } = await import("../build-
|
|
13
|
+
const { buildCommand } = await import("../build-DX6FLBMO.js");
|
|
13
14
|
await buildCommand({ target: options.target });
|
|
14
15
|
} catch (err) {
|
|
15
16
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -21,7 +22,7 @@ cli.command("build", "Build for production").option("--target <target>", "Deploy
|
|
|
21
22
|
});
|
|
22
23
|
cli.command("start", "Start production server").option("--port <port>", "Port number").action(async (options) => {
|
|
23
24
|
try {
|
|
24
|
-
const { startCommand } = await import("../start-
|
|
25
|
+
const { startCommand } = await import("../start-GJTYZIHY.js");
|
|
25
26
|
await startCommand({ port: options.port ? Number(options.port) : void 0 });
|
|
26
27
|
} catch (err) {
|
|
27
28
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -33,7 +34,7 @@ cli.command("start", "Start production server").option("--port <port>", "Port nu
|
|
|
33
34
|
});
|
|
34
35
|
cli.command("generate <type> <name>", "Generate a route, action, page, or ws endpoint").action(async (type, name) => {
|
|
35
36
|
try {
|
|
36
|
-
const { generateCommand } = await import("../generate-
|
|
37
|
+
const { generateCommand } = await import("../generate-MYEUR7I7.js");
|
|
37
38
|
await generateCommand(type, name);
|
|
38
39
|
} catch (err) {
|
|
39
40
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -45,7 +46,7 @@ cli.command("generate <type> <name>", "Generate a route, action, page, or ws end
|
|
|
45
46
|
});
|
|
46
47
|
cli.command("routes", "List all routes, actions, and WebSocket endpoints").action(async () => {
|
|
47
48
|
try {
|
|
48
|
-
const { routesCommand } = await import("../routes-
|
|
49
|
+
const { routesCommand } = await import("../routes-SDRMNHNM.js");
|
|
49
50
|
await routesCommand();
|
|
50
51
|
} catch (err) {
|
|
51
52
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -57,7 +58,7 @@ cli.command("routes", "List all routes, actions, and WebSocket endpoints").actio
|
|
|
57
58
|
});
|
|
58
59
|
cli.command("docker", "Generate Dockerfile for production").option("--force", "Overwrite existing Dockerfile").action(async (options) => {
|
|
59
60
|
try {
|
|
60
|
-
const { dockerCommand } = await import("../docker-
|
|
61
|
+
const { dockerCommand } = await import("../docker-NQ5NM4IW.js");
|
|
61
62
|
await dockerCommand({ force: options.force });
|
|
62
63
|
} catch (err) {
|
|
63
64
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["import cac from 'cac'\n\nconst cli = cac('theokit')\n\ncli\n .command('dev', 'Start development server')\n .option('--port <port>', 'Port number')\n .action(async (options) => {\n const { devCommand } = await import('./commands/dev.js')\n await devCommand({ port: options.port ? Number(options.port) : undefined })\n })\n\ncli\n .command('build', 'Build for production')\n .option('--target <target>', 'Deploy target (node, vercel, cloudflare)')\n .action(async (options) => {\n try {\n const { buildCommand } = await import('./commands/build.js')\n await buildCommand({ target: options.target })\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\ncli\n .command('start', 'Start production server')\n .option('--port <port>', 'Port number')\n .action(async (options) => {\n try {\n const { startCommand } = await import('./commands/start.js')\n await startCommand({ port: options.port ? Number(options.port) : undefined })\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\ncli\n .command('generate <type> <name>', 'Generate a route, action, page, or ws endpoint')\n .action(async (type: string, name: string) => {\n try {\n const { generateCommand } = await import('./commands/generate.js')\n await generateCommand(type, name)\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\ncli\n .command('routes', 'List all routes, actions, and WebSocket endpoints')\n .action(async () => {\n try {\n const { routesCommand } = await import('./commands/routes.js')\n await routesCommand()\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\ncli\n .command('docker', 'Generate Dockerfile for production')\n .option('--force', 'Overwrite existing Dockerfile')\n .action(async (options) => {\n try {\n const { dockerCommand } = await import('./commands/docker.js')\n await dockerCommand({ force: options.force })\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\ncli.help()\ncli.version('0.1.0-alpha.0')\n\nexport function main(): void {\n cli.parse()\n}\n\n// Auto-execute when run as script\nmain()\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["import cac from 'cac'\n\nconst cli = cac('theokit')\n\ncli\n .command('dev', 'Start development server')\n .option('--port <port>', 'Port number')\n .action(async (options) => {\n const { devCommand } = await import('./commands/dev.js')\n await devCommand({ port: options.port ? Number(options.port) : undefined })\n })\n\ncli\n .command('build', 'Build for production')\n .option('--target <target>', 'Deploy target (node, vercel, cloudflare)')\n .action(async (options) => {\n try {\n const { buildCommand } = await import('./commands/build.js')\n await buildCommand({ target: options.target })\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\ncli\n .command('start', 'Start production server')\n .option('--port <port>', 'Port number')\n .action(async (options) => {\n try {\n const { startCommand } = await import('./commands/start.js')\n await startCommand({ port: options.port ? Number(options.port) : undefined })\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\ncli\n .command('generate <type> <name>', 'Generate a route, action, page, or ws endpoint')\n .action(async (type: string, name: string) => {\n try {\n const { generateCommand } = await import('./commands/generate.js')\n await generateCommand(type, name)\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\ncli\n .command('routes', 'List all routes, actions, and WebSocket endpoints')\n .action(async () => {\n try {\n const { routesCommand } = await import('./commands/routes.js')\n await routesCommand()\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\ncli\n .command('docker', 'Generate Dockerfile for production')\n .option('--force', 'Overwrite existing Dockerfile')\n .action(async (options) => {\n try {\n const { dockerCommand } = await import('./commands/docker.js')\n await dockerCommand({ force: options.force })\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\ncli.help()\ncli.version('0.1.0-alpha.0')\n\nexport function main(): void {\n cli.parse()\n}\n\n// Auto-execute when run as script\nmain()\n"],"mappings":";;;;AAAA,OAAO,SAAS;AAEhB,IAAM,MAAM,IAAI,SAAS;AAEzB,IACG,QAAQ,OAAO,0BAA0B,EACzC,OAAO,iBAAiB,aAAa,EACrC,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,oBAAmB;AACvD,QAAM,WAAW,EAAE,MAAM,QAAQ,OAAO,OAAO,QAAQ,IAAI,IAAI,OAAU,CAAC;AAC5E,CAAC;AAEH,IACG,QAAQ,SAAS,sBAAsB,EACvC,OAAO,qBAAqB,0CAA0C,EACtE,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,sBAAqB;AAC3D,UAAM,aAAa,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAAA,EAC/C,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,CAAC;AAEH,IACG,QAAQ,SAAS,yBAAyB,EAC1C,OAAO,iBAAiB,aAAa,EACrC,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,sBAAqB;AAC3D,UAAM,aAAa,EAAE,MAAM,QAAQ,OAAO,OAAO,QAAQ,IAAI,IAAI,OAAU,CAAC;AAAA,EAC9E,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,CAAC;AAEH,IACG,QAAQ,0BAA0B,gDAAgD,EAClF,OAAO,OAAO,MAAc,SAAiB;AAC5C,MAAI;AACF,UAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,yBAAwB;AACjE,UAAM,gBAAgB,MAAM,IAAI;AAAA,EAClC,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,CAAC;AAEH,IACG,QAAQ,UAAU,mDAAmD,EACrE,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAsB;AAC7D,UAAM,cAAc;AAAA,EACtB,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,CAAC;AAEH,IACG,QAAQ,UAAU,oCAAoC,EACtD,OAAO,WAAW,+BAA+B,EACjD,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAsB;AAC7D,UAAM,cAAc,EAAE,OAAO,QAAQ,MAAM,CAAC;AAAA,EAC9C,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,CAAC;AAEH,IAAI,KAAK;AACT,IAAI,QAAQ,eAAe;AAEpB,SAAS,OAAa;AAC3B,MAAI,MAAM;AACZ;AAGA,KAAK;","names":[]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
nodeAdapter
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-F2FOMARQ.js";
|
|
6
|
+
import "./chunk-ZHZSF3AC.js";
|
|
7
|
+
import "./chunk-CDPRKHQ2.js";
|
|
8
|
+
import "./chunk-VXMNN5SA.js";
|
|
8
9
|
|
|
9
10
|
// src/adapters/cloudflare.ts
|
|
10
11
|
import { mkdirSync, writeFileSync } from "fs";
|
|
@@ -85,4 +86,4 @@ var cloudflareAdapter = {
|
|
|
85
86
|
export {
|
|
86
87
|
cloudflareAdapter
|
|
87
88
|
};
|
|
88
|
-
//# sourceMappingURL=cloudflare-
|
|
89
|
+
//# sourceMappingURL=cloudflare-UCG6CBPD.js.map
|
|
@@ -1 +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":"
|
|
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":[]}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
theoPlugin
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-ZHZSF3AC.js";
|
|
6
|
+
import "./chunk-CDPRKHQ2.js";
|
|
6
7
|
import {
|
|
7
8
|
validateProjectStructure
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-BJBRL6MW.js";
|
|
9
10
|
import {
|
|
10
11
|
loadConfig
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-4GCXBW7F.js";
|
|
13
|
+
import "./chunk-VXMNN5SA.js";
|
|
13
14
|
|
|
14
15
|
// src/cli/commands/dev.ts
|
|
15
16
|
import { createServer } from "vite";
|
|
@@ -44,4 +45,4 @@ export {
|
|
|
44
45
|
devCommand,
|
|
45
46
|
startDevServer
|
|
46
47
|
};
|
|
47
|
-
//# sourceMappingURL=dev-
|
|
48
|
+
//# sourceMappingURL=dev-E3LMBL7C.js.map
|
|
@@ -1 +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":"
|
|
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":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/cli/commands/docker.ts
|
|
4
5
|
import { existsSync, writeFileSync } from "fs";
|
|
@@ -98,4 +99,4 @@ async function dockerCommand(options) {
|
|
|
98
99
|
export {
|
|
99
100
|
dockerCommand
|
|
100
101
|
};
|
|
101
|
-
//# sourceMappingURL=docker-
|
|
102
|
+
//# sourceMappingURL=docker-NQ5NM4IW.js.map
|
|
@@ -1 +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":"
|
|
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":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
|
|
3
4
|
// src/cli/commands/generate.ts
|
|
4
5
|
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
@@ -113,4 +114,4 @@ async function generateCommand(type, name) {
|
|
|
113
114
|
export {
|
|
114
115
|
generateCommand
|
|
115
116
|
};
|
|
116
|
-
//# sourceMappingURL=generate-
|
|
117
|
+
//# sourceMappingURL=generate-MYEUR7I7.js.map
|
|
@@ -1 +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":"
|
|
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":[]}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
validateProjectStructure
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-BJBRL6MW.js";
|
|
5
6
|
import {
|
|
6
7
|
loadConfig
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4GCXBW7F.js";
|
|
8
9
|
import {
|
|
9
10
|
scanServerActions,
|
|
10
11
|
scanServerRoutes,
|
|
11
12
|
scanWebSocketRoutes
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VXMNN5SA.js";
|
|
13
14
|
|
|
14
15
|
// src/cli/commands/routes.ts
|
|
15
16
|
import { resolve, relative } from "path";
|
|
@@ -57,4 +58,4 @@ async function routesCommand() {
|
|
|
57
58
|
export {
|
|
58
59
|
routesCommand
|
|
59
60
|
};
|
|
60
|
-
//# sourceMappingURL=routes-
|
|
61
|
+
//# sourceMappingURL=routes-SDRMNHNM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/routes.ts"],"sourcesContent":["import { resolve, relative } from 'node:path'\nimport { loadConfig } from '../../config/load-config.js'\nimport { validateProjectStructure } from '../../core/validate-structure.js'\nimport { scanServerRoutes } from '../../server/scan.js'\nimport { scanServerActions } from '../../server/action-scan.js'\nimport { scanWebSocketRoutes } from '../../server/ws-scan.js'\n\nexport async function routesCommand(): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n const serverDir = resolve(cwd, config.serverDir)\n\n const apiRoutes = scanServerRoutes(serverDir)\n const actions = scanServerActions(serverDir)\n const wsRoutes = scanWebSocketRoutes(serverDir)\n\n const totalCount = apiRoutes.length + actions.length + wsRoutes.length\n\n if (totalCount === 0) {\n console.log('\\n No routes found.\\n')\n return\n }\n\n // API Routes\n if (apiRoutes.length > 0) {\n console.log('\\n API Routes')\n console.log(' ' + '─'.repeat(60))\n for (const route of apiRoutes) {\n const rel = relative(cwd, route.filePath)\n console.log(` GET/POST ${route.routePath.padEnd(30)} ${rel}`)\n }\n }\n\n // Actions\n if (actions.length > 0) {\n console.log('\\n Actions')\n console.log(' ' + '─'.repeat(60))\n for (const action of actions) {\n const rel = relative(cwd, action.filePath)\n console.log(` POST /api/__actions/${action.actionPath.padEnd(18)} ${rel}`)\n }\n }\n\n // WebSocket\n if (wsRoutes.length > 0) {\n console.log('\\n WebSocket')\n console.log(' ' + '─'.repeat(60))\n for (const route of wsRoutes) {\n const rel = relative(cwd, route.filePath)\n console.log(` WS ${route.wsPath.padEnd(30)} ${rel}`)\n }\n }\n\n console.log(`\\n Total: ${totalCount} endpoints\\n`)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/routes.ts"],"sourcesContent":["import { resolve, relative } from 'node:path'\nimport { loadConfig } from '../../config/load-config.js'\nimport { validateProjectStructure } from '../../core/validate-structure.js'\nimport { scanServerRoutes } from '../../server/scan.js'\nimport { scanServerActions } from '../../server/action-scan.js'\nimport { scanWebSocketRoutes } from '../../server/ws-scan.js'\n\nexport async function routesCommand(): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n const serverDir = resolve(cwd, config.serverDir)\n\n const apiRoutes = scanServerRoutes(serverDir)\n const actions = scanServerActions(serverDir)\n const wsRoutes = scanWebSocketRoutes(serverDir)\n\n const totalCount = apiRoutes.length + actions.length + wsRoutes.length\n\n if (totalCount === 0) {\n console.log('\\n No routes found.\\n')\n return\n }\n\n // API Routes\n if (apiRoutes.length > 0) {\n console.log('\\n API Routes')\n console.log(' ' + '─'.repeat(60))\n for (const route of apiRoutes) {\n const rel = relative(cwd, route.filePath)\n console.log(` GET/POST ${route.routePath.padEnd(30)} ${rel}`)\n }\n }\n\n // Actions\n if (actions.length > 0) {\n console.log('\\n Actions')\n console.log(' ' + '─'.repeat(60))\n for (const action of actions) {\n const rel = relative(cwd, action.filePath)\n console.log(` POST /api/__actions/${action.actionPath.padEnd(18)} ${rel}`)\n }\n }\n\n // WebSocket\n if (wsRoutes.length > 0) {\n console.log('\\n WebSocket')\n console.log(' ' + '─'.repeat(60))\n for (const route of wsRoutes) {\n const rel = relative(cwd, route.filePath)\n console.log(` WS ${route.wsPath.padEnd(30)} ${rel}`)\n }\n }\n\n console.log(`\\n Total: ${totalCount} endpoints\\n`)\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,SAAS,gBAAgB;AAOlC,eAAsB,gBAA+B;AACnD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,2BAAyB,GAAG;AAE5B,QAAM,YAAY,QAAQ,KAAK,OAAO,SAAS;AAE/C,QAAM,YAAY,iBAAiB,SAAS;AAC5C,QAAM,UAAU,kBAAkB,SAAS;AAC3C,QAAM,WAAW,oBAAoB,SAAS;AAE9C,QAAM,aAAa,UAAU,SAAS,QAAQ,SAAS,SAAS;AAEhE,MAAI,eAAe,GAAG;AACpB,YAAQ,IAAI,wBAAwB;AACpC;AAAA,EACF;AAGA,MAAI,UAAU,SAAS,GAAG;AACxB,YAAQ,IAAI,gBAAgB;AAC5B,YAAQ,IAAI,OAAO,SAAI,OAAO,EAAE,CAAC;AACjC,eAAW,SAAS,WAAW;AAC7B,YAAM,MAAM,SAAS,KAAK,MAAM,QAAQ;AACxC,cAAQ,IAAI,eAAe,MAAM,UAAU,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE;AAAA,IAChE;AAAA,EACF;AAGA,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAQ,IAAI,aAAa;AACzB,YAAQ,IAAI,OAAO,SAAI,OAAO,EAAE,CAAC;AACjC,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM,SAAS,KAAK,OAAO,QAAQ;AACzC,cAAQ,IAAI,8BAA8B,OAAO,WAAW,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE;AAAA,IACjF;AAAA,EACF;AAGA,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,OAAO,SAAI,OAAO,EAAE,CAAC;AACjC,eAAW,SAAS,UAAU;AAC5B,YAAM,MAAM,SAAS,KAAK,MAAM,QAAQ;AACxC,cAAQ,IAAI,eAAe,MAAM,OAAO,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE;AAAA,IAC7D;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,WAAc,UAAU;AAAA,CAAc;AACpD;","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
createProductionLoader,
|
|
4
5
|
createRateLimiter,
|
|
@@ -6,16 +7,16 @@ import {
|
|
|
6
7
|
executeRoute,
|
|
7
8
|
logRequest,
|
|
8
9
|
sendError
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CDPRKHQ2.js";
|
|
10
11
|
import {
|
|
11
12
|
loadConfig
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4GCXBW7F.js";
|
|
13
14
|
import {
|
|
14
15
|
matchRoute,
|
|
15
16
|
scanServerActions,
|
|
16
17
|
scanServerRoutes,
|
|
17
18
|
scanWebSocketRoutes
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VXMNN5SA.js";
|
|
19
20
|
|
|
20
21
|
// src/cli/commands/start.ts
|
|
21
22
|
import { createServer } from "http";
|
|
@@ -240,4 +241,4 @@ async function startCommand(options) {
|
|
|
240
241
|
export {
|
|
241
242
|
startCommand
|
|
242
243
|
};
|
|
243
|
-
//# sourceMappingURL=start-
|
|
244
|
+
//# sourceMappingURL=start-GJTYZIHY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/start.ts","../src/server/static.ts"],"sourcesContent":["import { createServer } from 'node:http'\nimport { randomUUID } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve, join, extname } from 'node:path'\nimport { loadConfig } from '../../config/load-config.js'\nimport { scanServerRoutes } from '../../server/scan.js'\nimport { scanServerActions } from '../../server/action-scan.js'\nimport { matchRoute } from '../../server/match.js'\nimport { executeRoute, sendError } from '../../server/execute.js'\nimport { executeAction } from '../../server/action-execute.js'\nimport { createProductionLoader } from '../../server/module-loader.js'\nimport { serveStaticFile } from '../../server/static.js'\nimport { logRequest } from '../../server/logger.js'\nimport { createRateLimiter } from '../../server/rate-limit.js'\nimport { scanWebSocketRoutes } from '../../server/ws-scan.js'\n\ninterface StartOptions {\n port?: number\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n\n const distDir = resolve(cwd, '.theo')\n const clientDir = resolve(distDir, 'client')\n const serverDir = resolve(cwd, 'server')\n\n if (!existsSync(clientDir)) {\n throw new Error('No build found. Run `theo build` first.')\n }\n\n const indexHtml = readFileSync(join(clientDir, 'index.html'), 'utf-8')\n const loadModule = createProductionLoader()\n const port = options.port ?? config.port\n\n // Custom error pages (optional)\n const custom404Path = join(clientDir, '404.html')\n const custom500Path = join(clientDir, '500.html')\n const custom404Html = existsSync(custom404Path) ? readFileSync(custom404Path, 'utf-8') : null\n const custom500Html = existsSync(custom500Path) ? readFileSync(custom500Path, 'utf-8') : null\n\n // Rate limiter (opt-in)\n const rateLimiter = config.rateLimit\n ? createRateLimiter(config.rateLimit)\n : null\n\n // SSR setup (opt-in)\n const ssrServerPath = resolve(distDir, 'server/entry-server.js')\n const ssrEnabled = config.ssr && existsSync(ssrServerPath)\n let ssrRender: ((url: string) => Promise<string | { redirect: Response }>) | null = null\n let htmlHead = ''\n let htmlTail = ''\n\n if (ssrEnabled) {\n const mod = await import(ssrServerPath)\n ssrRender = mod.render\n // Split HTML template on root div\n const rootDivMatch = indexHtml.match(/<div id=[\"']root[\"'][^>]*>/)\n if (rootDivMatch) {\n const splitIdx = indexHtml.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n htmlHead = indexHtml.slice(0, splitIdx)\n htmlTail = indexHtml.slice(splitIdx)\n }\n }\n\n const server = createServer(async (req, res) => {\n const url = req.url ?? '/'\n const requestId = randomUUID()\n const start = Date.now()\n\n try {\n // 1. Action routes\n if (url.startsWith('/api/__actions/')) {\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const pathAfterPrefix = url.slice('/api/__actions/'.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n if (segments.length < 2) {\n sendError(res, 'BAD_REQUEST', 'Action URL must be /api/__actions/{file}/{exportName}', 400, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 400, duration: Date.now() - start, requestId })\n return\n }\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n const actions = scanServerActions(serverDir)\n const action = actions.find((a) => a.actionPath === actionPath)\n if (!action) {\n sendError(res, 'NOT_FOUND', `Action \"${actionPath}\" not found`, 404, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n await executeAction(action.filePath, exportName, req, res, loadModule, serverDir, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: res.statusCode, duration: Date.now() - start, requestId })\n return\n }\n\n // 2. API routes\n if (url.startsWith('/api/')) {\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const routes = scanServerRoutes(serverDir)\n const match = matchRoute(url, routes)\n if (!match) {\n sendError(res, 'NOT_FOUND', 'API route not found', 404, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n const method = (req.method ?? 'GET').toUpperCase()\n await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir, requestId)\n logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId })\n return\n }\n\n // 3. Static files\n if (serveStaticFile(req, res, clientDir)) return\n\n // 4. Custom 404 for URLs with file extensions (missing static files)\n const urlPath = url.split('?')[0]\n if (custom404Html && extname(urlPath)) {\n res.writeHead(404, { 'Content-Type': 'text/html' })\n res.end(custom404Html)\n return\n }\n\n // 5. SSR or SPA fallback\n if (ssrRender) {\n try {\n const result = await ssrRender(url)\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n const ssrHtml = result as string\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(htmlHead + ssrHtml + htmlTail)\n return\n } catch (ssrErr) {\n console.error('[SSR Error] Falling back to CSR:', (ssrErr as Error).message)\n // Fall through to CSR fallback\n }\n }\n\n // CSR fallback\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(indexHtml)\n } catch (err) {\n // Custom 500 page for non-API errors\n if (custom500Html && !res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n res.end(custom500Html)\n } else if (!res.headersSent) {\n sendError(res, 'INTERNAL_ERROR', (err as Error).message, 500)\n } else {\n res.end()\n }\n }\n })\n\n // WebSocket upgrade handler (opt-in: only if server/ws/ exists)\n const wsRoutes = scanWebSocketRoutes(serverDir)\n if (wsRoutes.length > 0) {\n try {\n const { WebSocketServer } = await import('ws')\n const wss = new WebSocketServer({ noServer: true })\n\n server.on('upgrade', async (request, socket, head) => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) {\n socket.destroy()\n return\n }\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find(r => r.wsPath === wsPath)\n if (!match) {\n socket.destroy()\n return\n }\n\n try {\n const mod = await loadModule(match.filePath)\n const handler = (mod.default ?? mod) as import('../../server/define-websocket.js').WebSocketHandler\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => handler.onMessage?.(ws, data.toString()))\n ws.on('close', (code: number, reason: Buffer) => handler.onClose?.(ws, code, reason))\n ws.on('error', (err: Error) => handler.onError?.(ws, err))\n })\n } catch {\n socket.destroy()\n }\n })\n } catch {\n throw new Error(\n 'WebSocket routes found but \"ws\" package is not installed. Run: npm install ws',\n )\n }\n }\n\n server.listen(port, () => {\n console.log(`\\n Theo production server`)\n console.log(` → http://localhost:${port}\\n`)\n })\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { resolve, extname } from 'node:path'\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain',\n '.map': 'application/json',\n}\n\nexport function serveStaticFile(\n req: IncomingMessage,\n res: ServerResponse,\n clientDir: string,\n): boolean {\n const urlPath = (req.url ?? '/').split('?')[0]\n\n // Path traversal prevention (EC-1)\n const filePath = resolve(clientDir, '.' + urlPath)\n if (!filePath.startsWith(clientDir)) {\n res.writeHead(403)\n res.end('Forbidden')\n return true\n }\n\n if (!existsSync(filePath)) return false\n\n const stat = statSync(filePath)\n if (!stat.isFile()) return false\n\n const ext = extname(filePath)\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream'\n const content = readFileSync(filePath)\n\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': content.length,\n })\n res.end(content)\n return true\n}\n"],"mappings":"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","names":["existsSync","readFileSync","resolve","extname","resolve","existsSync","readFileSync","extname"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/start.ts","../src/server/static.ts"],"sourcesContent":["import { createServer } from 'node:http'\nimport { randomUUID } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve, join, extname } from 'node:path'\nimport { loadConfig } from '../../config/load-config.js'\nimport { scanServerRoutes } from '../../server/scan.js'\nimport { scanServerActions } from '../../server/action-scan.js'\nimport { matchRoute } from '../../server/match.js'\nimport { executeRoute, sendError } from '../../server/execute.js'\nimport { executeAction } from '../../server/action-execute.js'\nimport { createProductionLoader } from '../../server/module-loader.js'\nimport { serveStaticFile } from '../../server/static.js'\nimport { logRequest } from '../../server/logger.js'\nimport { createRateLimiter } from '../../server/rate-limit.js'\nimport { scanWebSocketRoutes } from '../../server/ws-scan.js'\n\ninterface StartOptions {\n port?: number\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const cwd = process.cwd()\n const config = await loadConfig(cwd)\n\n const distDir = resolve(cwd, '.theo')\n const clientDir = resolve(distDir, 'client')\n const serverDir = resolve(cwd, 'server')\n\n if (!existsSync(clientDir)) {\n throw new Error('No build found. Run `theo build` first.')\n }\n\n const indexHtml = readFileSync(join(clientDir, 'index.html'), 'utf-8')\n const loadModule = createProductionLoader()\n const port = options.port ?? config.port\n\n // Custom error pages (optional)\n const custom404Path = join(clientDir, '404.html')\n const custom500Path = join(clientDir, '500.html')\n const custom404Html = existsSync(custom404Path) ? readFileSync(custom404Path, 'utf-8') : null\n const custom500Html = existsSync(custom500Path) ? readFileSync(custom500Path, 'utf-8') : null\n\n // Rate limiter (opt-in)\n const rateLimiter = config.rateLimit\n ? createRateLimiter(config.rateLimit)\n : null\n\n // SSR setup (opt-in)\n const ssrServerPath = resolve(distDir, 'server/entry-server.js')\n const ssrEnabled = config.ssr && existsSync(ssrServerPath)\n let ssrRender: ((url: string) => Promise<string | { redirect: Response }>) | null = null\n let htmlHead = ''\n let htmlTail = ''\n\n if (ssrEnabled) {\n const mod = await import(ssrServerPath)\n ssrRender = mod.render\n // Split HTML template on root div\n const rootDivMatch = indexHtml.match(/<div id=[\"']root[\"'][^>]*>/)\n if (rootDivMatch) {\n const splitIdx = indexHtml.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n htmlHead = indexHtml.slice(0, splitIdx)\n htmlTail = indexHtml.slice(splitIdx)\n }\n }\n\n const server = createServer(async (req, res) => {\n const url = req.url ?? '/'\n const requestId = randomUUID()\n const start = Date.now()\n\n try {\n // 1. Action routes\n if (url.startsWith('/api/__actions/')) {\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const pathAfterPrefix = url.slice('/api/__actions/'.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n if (segments.length < 2) {\n sendError(res, 'BAD_REQUEST', 'Action URL must be /api/__actions/{file}/{exportName}', 400, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 400, duration: Date.now() - start, requestId })\n return\n }\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n const actions = scanServerActions(serverDir)\n const action = actions.find((a) => a.actionPath === actionPath)\n if (!action) {\n sendError(res, 'NOT_FOUND', `Action \"${actionPath}\" not found`, 404, undefined, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n await executeAction(action.filePath, exportName, req, res, loadModule, serverDir, requestId)\n logRequest({ method: req.method ?? 'POST', url, status: res.statusCode, duration: Date.now() - start, requestId })\n return\n }\n\n // 2. API routes\n if (url.startsWith('/api/')) {\n res.setHeader('x-request-id', requestId)\n\n // Rate limit check\n if (rateLimiter) {\n const check = rateLimiter(req)\n for (const [k, v] of Object.entries(check.headers)) res.setHeader(k, v)\n if (check.limited) {\n sendError(res, 'RATE_LIMITED', 'Too many requests', 429, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 429, duration: Date.now() - start, requestId })\n return\n }\n }\n\n const routes = scanServerRoutes(serverDir)\n const match = matchRoute(url, routes)\n if (!match) {\n sendError(res, 'NOT_FOUND', 'API route not found', 404, undefined, requestId)\n logRequest({ method: req.method ?? 'GET', url, status: 404, duration: Date.now() - start, requestId })\n return\n }\n const method = (req.method ?? 'GET').toUpperCase()\n await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir, requestId)\n logRequest({ method, url, status: res.statusCode, duration: Date.now() - start, requestId })\n return\n }\n\n // 3. Static files\n if (serveStaticFile(req, res, clientDir)) return\n\n // 4. Custom 404 for URLs with file extensions (missing static files)\n const urlPath = url.split('?')[0]\n if (custom404Html && extname(urlPath)) {\n res.writeHead(404, { 'Content-Type': 'text/html' })\n res.end(custom404Html)\n return\n }\n\n // 5. SSR or SPA fallback\n if (ssrRender) {\n try {\n const result = await ssrRender(url)\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n const ssrHtml = result as string\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(htmlHead + ssrHtml + htmlTail)\n return\n } catch (ssrErr) {\n console.error('[SSR Error] Falling back to CSR:', (ssrErr as Error).message)\n // Fall through to CSR fallback\n }\n }\n\n // CSR fallback\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(indexHtml)\n } catch (err) {\n // Custom 500 page for non-API errors\n if (custom500Html && !res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n res.end(custom500Html)\n } else if (!res.headersSent) {\n sendError(res, 'INTERNAL_ERROR', (err as Error).message, 500)\n } else {\n res.end()\n }\n }\n })\n\n // WebSocket upgrade handler (opt-in: only if server/ws/ exists)\n const wsRoutes = scanWebSocketRoutes(serverDir)\n if (wsRoutes.length > 0) {\n try {\n const { WebSocketServer } = await import('ws')\n const wss = new WebSocketServer({ noServer: true })\n\n server.on('upgrade', async (request, socket, head) => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) {\n socket.destroy()\n return\n }\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find(r => r.wsPath === wsPath)\n if (!match) {\n socket.destroy()\n return\n }\n\n try {\n const mod = await loadModule(match.filePath)\n const handler = (mod.default ?? mod) as import('../../server/define-websocket.js').WebSocketHandler\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => handler.onMessage?.(ws, data.toString()))\n ws.on('close', (code: number, reason: Buffer) => handler.onClose?.(ws, code, reason))\n ws.on('error', (err: Error) => handler.onError?.(ws, err))\n })\n } catch {\n socket.destroy()\n }\n })\n } catch {\n throw new Error(\n 'WebSocket routes found but \"ws\" package is not installed. Run: npm install ws',\n )\n }\n }\n\n server.listen(port, () => {\n console.log(`\\n Theo production server`)\n console.log(` → http://localhost:${port}\\n`)\n })\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { resolve, extname } from 'node:path'\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain',\n '.map': 'application/json',\n}\n\nexport function serveStaticFile(\n req: IncomingMessage,\n res: ServerResponse,\n clientDir: string,\n): boolean {\n const urlPath = (req.url ?? '/').split('?')[0]\n\n // Path traversal prevention (EC-1)\n const filePath = resolve(clientDir, '.' + urlPath)\n if (!filePath.startsWith(clientDir)) {\n res.writeHead(403)\n res.end('Forbidden')\n return true\n }\n\n if (!existsSync(filePath)) return false\n\n const stat = statSync(filePath)\n if (!stat.isFile()) return false\n\n const ext = extname(filePath)\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream'\n const content = readFileSync(filePath)\n\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': content.length,\n })\n res.end(content)\n return true\n}\n"],"mappings":"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","names":["existsSync","readFileSync","resolve","extname","resolve","existsSync","readFileSync","extname"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
2
3
|
import {
|
|
3
4
|
nodeAdapter
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-F2FOMARQ.js";
|
|
6
|
+
import "./chunk-ZHZSF3AC.js";
|
|
7
|
+
import "./chunk-CDPRKHQ2.js";
|
|
8
|
+
import "./chunk-VXMNN5SA.js";
|
|
8
9
|
|
|
9
10
|
// src/adapters/vercel.ts
|
|
10
11
|
import { existsSync, mkdirSync, writeFileSync, cpSync } from "fs";
|
|
@@ -77,4 +78,4 @@ var vercelAdapter = {
|
|
|
77
78
|
export {
|
|
78
79
|
vercelAdapter
|
|
79
80
|
};
|
|
80
|
-
//# sourceMappingURL=vercel-
|
|
81
|
+
//# sourceMappingURL=vercel-K4OINXCD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/vercel.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync, cpSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport type { DeployAdapter } from './types.js'\nimport type { TheoConfig } from '../config/schema.js'\nimport { nodeAdapter } from './node.js'\n\nexport const vercelAdapter: DeployAdapter = {\n name: 'vercel',\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 clientDir = resolve(cwd, '.theo/client')\n const outputDir = resolve(cwd, '.vercel/output')\n\n // 2. Create .vercel/output structure\n mkdirSync(resolve(outputDir, 'static'), { recursive: true })\n mkdirSync(resolve(outputDir, 'functions/api.func'), { recursive: true })\n\n // 3. Copy static assets\n if (existsSync(clientDir)) {\n cpSync(clientDir, resolve(outputDir, 'static'), { recursive: true })\n }\n\n // 4. Generate serverless function entry\n // This wraps Theo's production server handler for Vercel's serverless runtime\n const functionEntry = [\n `// Generated by Theo — Vercel Adapter`,\n `// Environment variables are resolved at RUNTIME, not build time (EC-1)`,\n `export default async function handler(req, res) {`,\n ` const { createServer } = await import('node:http');`,\n ` const { resolve } = await import('node:path');`,\n ` const { existsSync } = await import('node:fs');`,\n ` const serverDir = resolve(process.cwd(), 'server');`,\n ` // Lazy-load Theo server modules`,\n ` const { scanServerRoutes } = await import('./theo-server/scan.js');`,\n ` const { matchRoute } = await import('./theo-server/match.js');`,\n ` const { executeRoute, sendError } = await import('./theo-server/execute.js');`,\n ` const { createProductionLoader } = await import('./theo-server/module-loader.js');`,\n ` const loadModule = createProductionLoader();`,\n ` const url = req.url ?? '/';`,\n ` if (url.startsWith('/api/')) {`,\n ` const routes = scanServerRoutes(serverDir);`,\n ` const match = matchRoute(url, routes);`,\n ` if (!match) { sendError(res, 'NOT_FOUND', 'API route not found', 404); return; }`,\n ` const method = (req.method ?? 'GET').toUpperCase();`,\n ` await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir);`,\n ` return;`,\n ` }`,\n ` // SPA fallback — Vercel serves static index.html via routing`,\n ` res.writeHead(200, { 'Content-Type': 'text/html' });`,\n ` res.end('<html><body>SPA fallback</body></html>');`,\n `}`,\n ].join('\\n')\n\n writeFileSync(resolve(outputDir, 'functions/api.func/index.mjs'), functionEntry)\n\n // 5. Generate .vc-config.json\n const vcConfig = {\n runtime: 'nodejs22.x',\n handler: 'index.mjs',\n launcherType: 'Nodejs',\n shouldAddHelpers: true,\n }\n writeFileSync(\n resolve(outputDir, 'functions/api.func/.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n )\n\n // 6. Generate config.json (routing)\n const routingConfig = {\n version: 3,\n routes: [\n { src: '/api/(.*)', dest: '/api' },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index.html' },\n ],\n }\n writeFileSync(\n resolve(outputDir, 'config.json'),\n JSON.stringify(routingConfig, null, 2),\n )\n\n console.log('\\n ✓ Vercel output → .vercel/output/\\n')\n },\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/vercel.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync, cpSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport type { DeployAdapter } from './types.js'\nimport type { TheoConfig } from '../config/schema.js'\nimport { nodeAdapter } from './node.js'\n\nexport const vercelAdapter: DeployAdapter = {\n name: 'vercel',\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 clientDir = resolve(cwd, '.theo/client')\n const outputDir = resolve(cwd, '.vercel/output')\n\n // 2. Create .vercel/output structure\n mkdirSync(resolve(outputDir, 'static'), { recursive: true })\n mkdirSync(resolve(outputDir, 'functions/api.func'), { recursive: true })\n\n // 3. Copy static assets\n if (existsSync(clientDir)) {\n cpSync(clientDir, resolve(outputDir, 'static'), { recursive: true })\n }\n\n // 4. Generate serverless function entry\n // This wraps Theo's production server handler for Vercel's serverless runtime\n const functionEntry = [\n `// Generated by Theo — Vercel Adapter`,\n `// Environment variables are resolved at RUNTIME, not build time (EC-1)`,\n `export default async function handler(req, res) {`,\n ` const { createServer } = await import('node:http');`,\n ` const { resolve } = await import('node:path');`,\n ` const { existsSync } = await import('node:fs');`,\n ` const serverDir = resolve(process.cwd(), 'server');`,\n ` // Lazy-load Theo server modules`,\n ` const { scanServerRoutes } = await import('./theo-server/scan.js');`,\n ` const { matchRoute } = await import('./theo-server/match.js');`,\n ` const { executeRoute, sendError } = await import('./theo-server/execute.js');`,\n ` const { createProductionLoader } = await import('./theo-server/module-loader.js');`,\n ` const loadModule = createProductionLoader();`,\n ` const url = req.url ?? '/';`,\n ` if (url.startsWith('/api/')) {`,\n ` const routes = scanServerRoutes(serverDir);`,\n ` const match = matchRoute(url, routes);`,\n ` if (!match) { sendError(res, 'NOT_FOUND', 'API route not found', 404); return; }`,\n ` const method = (req.method ?? 'GET').toUpperCase();`,\n ` await executeRoute(match.route, method, match.params, req, res, loadModule, serverDir);`,\n ` return;`,\n ` }`,\n ` // SPA fallback — Vercel serves static index.html via routing`,\n ` res.writeHead(200, { 'Content-Type': 'text/html' });`,\n ` res.end('<html><body>SPA fallback</body></html>');`,\n `}`,\n ].join('\\n')\n\n writeFileSync(resolve(outputDir, 'functions/api.func/index.mjs'), functionEntry)\n\n // 5. Generate .vc-config.json\n const vcConfig = {\n runtime: 'nodejs22.x',\n handler: 'index.mjs',\n launcherType: 'Nodejs',\n shouldAddHelpers: true,\n }\n writeFileSync(\n resolve(outputDir, 'functions/api.func/.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n )\n\n // 6. Generate config.json (routing)\n const routingConfig = {\n version: 3,\n routes: [\n { src: '/api/(.*)', dest: '/api' },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index.html' },\n ],\n }\n writeFileSync(\n resolve(outputDir, 'config.json'),\n JSON.stringify(routingConfig, null, 2),\n )\n\n console.log('\\n ✓ Vercel output → .vercel/output/\\n')\n },\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,YAAY,WAAW,eAAe,cAAc;AAC7D,SAAS,eAAqB;AAKvB,IAAM,gBAA+B;AAAA,EAC1C,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAA4B;AAE1D,UAAM,YAAY,MAAM,QAAQ,GAAG;AAEnC,UAAM,YAAY,QAAQ,KAAK,cAAc;AAC7C,UAAM,YAAY,QAAQ,KAAK,gBAAgB;AAG/C,cAAU,QAAQ,WAAW,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,cAAU,QAAQ,WAAW,oBAAoB,GAAG,EAAE,WAAW,KAAK,CAAC;AAGvE,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,WAAW,QAAQ,WAAW,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IACrE;AAIA,UAAM,gBAAgB;AAAA,MACpB;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,8BAA8B,GAAG,aAAa;AAG/E,UAAM,WAAW;AAAA,MACf,SAAS;AAAA,MACT,SAAS;AAAA,MACT,cAAc;AAAA,MACd,kBAAkB;AAAA,IACpB;AACA;AAAA,MACE,QAAQ,WAAW,oCAAoC;AAAA,MACvD,KAAK,UAAU,UAAU,MAAM,CAAC;AAAA,IAClC;AAGA,UAAM,gBAAgB;AAAA,MACpB,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,EAAE,KAAK,aAAa,MAAM,OAAO;AAAA,QACjC,EAAE,QAAQ,aAAa;AAAA,QACvB,EAAE,KAAK,SAAS,MAAM,cAAc;AAAA,MACtC;AAAA,IACF;AACA;AAAA,MACE,QAAQ,WAAW,aAAa;AAAA,MAChC,KAAK,UAAU,eAAe,MAAM,CAAC;AAAA,IACvC;AAEA,YAAQ,IAAI,mDAAyC;AAAA,EACvD;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theokit",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"cac": "^6.7.14",
|
|
34
|
+
"tsx": "^4.19.0",
|
|
34
35
|
"vite": "^6.0.0",
|
|
35
36
|
"@vitejs/plugin-react": "^4.4.0"
|
|
36
37
|
},
|