theokit 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-NSKFOAFX.js +49 -0
- package/dist/build-NSKFOAFX.js.map +1 -0
- package/dist/chunk-ASGEGAWL.js +480 -0
- package/dist/chunk-ASGEGAWL.js.map +1 -0
- package/dist/chunk-ATSTRYYT.js +894 -0
- package/dist/chunk-ATSTRYYT.js.map +1 -0
- package/dist/chunk-KPU44T6G.js +71 -0
- package/dist/chunk-KPU44T6G.js.map +1 -0
- package/dist/chunk-MMZZBPMX.js +335 -0
- package/dist/chunk-MMZZBPMX.js.map +1 -0
- package/dist/chunk-N5YH2UDG.js +85 -0
- package/dist/chunk-N5YH2UDG.js.map +1 -0
- package/dist/chunk-SAVVU5LG.js +66 -0
- package/dist/chunk-SAVVU5LG.js.map +1 -0
- package/dist/chunk-TXMUCDJT.js +43 -0
- package/dist/chunk-TXMUCDJT.js.map +1 -0
- package/dist/chunk-U3OJFWK3.js +162 -0
- package/dist/chunk-U3OJFWK3.js.map +1 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +37 -0
- package/dist/client/index.js +57 -0
- package/dist/client/index.js.map +1 -0
- package/dist/cloudflare-CVGN7FOU.js +88 -0
- package/dist/cloudflare-CVGN7FOU.js.map +1 -0
- package/dist/dev-7UJK3M2O.js +47 -0
- package/dist/dev-7UJK3M2O.js.map +1 -0
- package/dist/docker-M253W54T.js +101 -0
- package/dist/docker-M253W54T.js.map +1 -0
- package/dist/generate-AA7ZE42F.js +116 -0
- package/dist/generate-AA7ZE42F.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limit-C6hHXIj1.d.ts +13 -0
- package/dist/routes-YP357VAC.js +60 -0
- package/dist/routes-YP357VAC.js.map +1 -0
- package/dist/server/index.d.ts +94 -0
- package/dist/server/index.js +148 -0
- package/dist/server/index.js.map +1 -0
- package/dist/start-BIS3RCFQ.js +243 -0
- package/dist/start-BIS3RCFQ.js.map +1 -0
- package/dist/vercel-747SR2FB.js +80 -0
- package/dist/vercel-747SR2FB.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +12 -0
- package/dist/vite-plugin/index.js +8 -0
- package/dist/vite-plugin/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config/load-config.ts
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
|
|
8
|
+
// src/config/schema.ts
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var rateLimitSchema = z.object({
|
|
11
|
+
windowMs: z.number().min(1),
|
|
12
|
+
max: z.number().int().min(1)
|
|
13
|
+
});
|
|
14
|
+
var theoConfigSchema = z.object({
|
|
15
|
+
appDir: z.string().default("app"),
|
|
16
|
+
serverDir: z.string().default("server"),
|
|
17
|
+
port: z.number().int().min(1).max(65535).default(3e3),
|
|
18
|
+
ssr: z.boolean().default(false),
|
|
19
|
+
rateLimit: rateLimitSchema.optional()
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// src/config/errors.ts
|
|
23
|
+
var TheoConfigError = class extends Error {
|
|
24
|
+
issues;
|
|
25
|
+
configPath;
|
|
26
|
+
constructor(issues, configPath) {
|
|
27
|
+
const issueLines = issues.map((i) => ` - ${i.field}: ${i.message}`).join("\n");
|
|
28
|
+
super(
|
|
29
|
+
`Invalid theo.config.ts
|
|
30
|
+
|
|
31
|
+
File: ${configPath}
|
|
32
|
+
|
|
33
|
+
` + (issueLines ? ` Issues:
|
|
34
|
+
${issueLines}
|
|
35
|
+
` : "")
|
|
36
|
+
);
|
|
37
|
+
this.name = "TheoConfigError";
|
|
38
|
+
this.issues = issues;
|
|
39
|
+
this.configPath = configPath;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/config/load-config.ts
|
|
44
|
+
var CONFIG_FILE = "theo.config.ts";
|
|
45
|
+
async function loadConfig(dir) {
|
|
46
|
+
const configPath = resolve(dir, CONFIG_FILE);
|
|
47
|
+
if (!existsSync(configPath)) {
|
|
48
|
+
return theoConfigSchema.parse({});
|
|
49
|
+
}
|
|
50
|
+
let mod;
|
|
51
|
+
try {
|
|
52
|
+
mod = await import(pathToFileURL(configPath).href);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throw new TheoConfigError(
|
|
55
|
+
[{ field: "_file", message: err.message }],
|
|
56
|
+
configPath
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const userConfig = mod.default;
|
|
60
|
+
if (userConfig == null || typeof userConfig !== "object") {
|
|
61
|
+
throw new TheoConfigError(
|
|
62
|
+
[
|
|
63
|
+
{
|
|
64
|
+
field: "_export",
|
|
65
|
+
message: "theo.config.ts must use export default defineConfig({...})"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
configPath
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const result = theoConfigSchema.safeParse(userConfig);
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
const issues = result.error.issues.map((i) => ({
|
|
74
|
+
field: i.path.join("."),
|
|
75
|
+
message: i.message
|
|
76
|
+
}));
|
|
77
|
+
throw new TheoConfigError(issues, configPath);
|
|
78
|
+
}
|
|
79
|
+
return result.data;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
loadConfig
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=chunk-N5YH2UDG.js.map
|
|
@@ -0,0 +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":";;;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":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/server/auth.ts
|
|
2
|
+
var AuthRequiredError = class extends Error {
|
|
3
|
+
code = "AUTH_REQUIRED";
|
|
4
|
+
status = 401;
|
|
5
|
+
constructor(message = "Authentication required") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "AuthRequiredError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
function requireAuth(session) {
|
|
11
|
+
if (session === null || session === void 0) {
|
|
12
|
+
throw new AuthRequiredError();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/server/rate-limit.ts
|
|
17
|
+
function createRateLimiter(config) {
|
|
18
|
+
const store = /* @__PURE__ */ new Map();
|
|
19
|
+
let checkCount = 0;
|
|
20
|
+
return function checkRateLimit(req) {
|
|
21
|
+
const key = req.socket?.remoteAddress ?? "unknown";
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
if (++checkCount % 1e3 === 0) {
|
|
24
|
+
for (const [k, v] of store) {
|
|
25
|
+
if (v.resetAt < now) store.delete(k);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const entry = store.get(key);
|
|
29
|
+
if (!entry || now > entry.resetAt) {
|
|
30
|
+
store.set(key, { count: 1, resetAt: now + config.windowMs });
|
|
31
|
+
return {
|
|
32
|
+
limited: false,
|
|
33
|
+
headers: {
|
|
34
|
+
"X-RateLimit-Limit": String(config.max),
|
|
35
|
+
"X-RateLimit-Remaining": String(config.max - 1)
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
entry.count++;
|
|
40
|
+
if (entry.count > config.max) {
|
|
41
|
+
const retryAfter = Math.ceil((entry.resetAt - now) / 1e3);
|
|
42
|
+
return {
|
|
43
|
+
limited: true,
|
|
44
|
+
headers: {
|
|
45
|
+
"X-RateLimit-Limit": String(config.max),
|
|
46
|
+
"X-RateLimit-Remaining": "0",
|
|
47
|
+
"Retry-After": String(retryAfter)
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
limited: false,
|
|
53
|
+
headers: {
|
|
54
|
+
"X-RateLimit-Limit": String(config.max),
|
|
55
|
+
"X-RateLimit-Remaining": String(config.max - entry.count)
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
AuthRequiredError,
|
|
63
|
+
requireAuth,
|
|
64
|
+
createRateLimiter
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=chunk-SAVVU5LG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/auth.ts","../src/server/rate-limit.ts"],"sourcesContent":["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 } 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"],"mappings":";AAAO,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;AAEO,SAAS,YAAe,SAAqD;AAClF,MAAI,YAAY,QAAQ,YAAY,QAAW;AAC7C,UAAM,IAAI,kBAAkB;AAAA,EAC9B;AACF;;;ACGO,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;","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
theoPlugin
|
|
4
|
+
} from "./chunk-ASGEGAWL.js";
|
|
5
|
+
|
|
6
|
+
// src/adapters/node.ts
|
|
7
|
+
import { build as viteBuild } from "vite";
|
|
8
|
+
import react from "@vitejs/plugin-react";
|
|
9
|
+
var nodeAdapter = {
|
|
10
|
+
name: "node",
|
|
11
|
+
async build(config, cwd) {
|
|
12
|
+
await viteBuild({
|
|
13
|
+
root: cwd,
|
|
14
|
+
plugins: [react(), theoPlugin({ root: cwd, ssr: config.ssr })],
|
|
15
|
+
build: {
|
|
16
|
+
outDir: ".theo/client",
|
|
17
|
+
emptyOutDir: true
|
|
18
|
+
},
|
|
19
|
+
logLevel: "info"
|
|
20
|
+
});
|
|
21
|
+
if (config.ssr) {
|
|
22
|
+
console.log("\n Building SSR...\n");
|
|
23
|
+
await viteBuild({
|
|
24
|
+
root: cwd,
|
|
25
|
+
plugins: [react(), theoPlugin({ root: cwd, ssr: true })],
|
|
26
|
+
build: {
|
|
27
|
+
ssr: true,
|
|
28
|
+
outDir: ".theo/server",
|
|
29
|
+
emptyOutDir: true,
|
|
30
|
+
rollupOptions: {
|
|
31
|
+
input: "/@theo/entry-server"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
logLevel: "info"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
nodeAdapter
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=chunk-TXMUCDJT.js.map
|
|
@@ -0,0 +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":";;;;;;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":[]}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/server/match.ts
|
|
4
|
+
function compilePattern(routePath) {
|
|
5
|
+
const paramNames = [];
|
|
6
|
+
const regexStr = routePath.replace(/:([^/]+)/g, (_, name) => {
|
|
7
|
+
paramNames.push(name);
|
|
8
|
+
return "([^/]+)";
|
|
9
|
+
});
|
|
10
|
+
return { pattern: new RegExp(`^${regexStr}$`), paramNames };
|
|
11
|
+
}
|
|
12
|
+
function matchRoute(url, routes) {
|
|
13
|
+
let path = url.split("?")[0];
|
|
14
|
+
if (path.length > 1 && path.endsWith("/")) {
|
|
15
|
+
path = path.slice(0, -1);
|
|
16
|
+
}
|
|
17
|
+
for (const route of routes) {
|
|
18
|
+
const match = route.pattern.exec(path);
|
|
19
|
+
if (match) {
|
|
20
|
+
const params = {};
|
|
21
|
+
route.paramNames.forEach((name, i) => {
|
|
22
|
+
params[name] = match[i + 1];
|
|
23
|
+
});
|
|
24
|
+
return { route, params };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/server/scan.ts
|
|
31
|
+
import { readdirSync, existsSync, statSync } from "fs";
|
|
32
|
+
import { join, resolve, relative, extname } from "path";
|
|
33
|
+
var ROUTE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
34
|
+
function fileToRoutePath(filePath, routesDir) {
|
|
35
|
+
let rel = relative(routesDir, filePath);
|
|
36
|
+
const ext = extname(rel);
|
|
37
|
+
rel = rel.slice(0, -ext.length);
|
|
38
|
+
rel = rel.replace(/\\/g, "/");
|
|
39
|
+
if (rel.endsWith("/index")) {
|
|
40
|
+
rel = rel.slice(0, -6);
|
|
41
|
+
} else if (rel === "index") {
|
|
42
|
+
rel = "";
|
|
43
|
+
}
|
|
44
|
+
rel = rel.replace(/\[([^\]]+)\]/g, ":$1");
|
|
45
|
+
return `/api/${rel}`;
|
|
46
|
+
}
|
|
47
|
+
function scanDir(dir, routesDir, results) {
|
|
48
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const fullPath = join(dir, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
if (!entry.name.startsWith("_") && !entry.name.startsWith(".")) {
|
|
53
|
+
scanDir(fullPath, routesDir, results);
|
|
54
|
+
}
|
|
55
|
+
} else if (entry.isFile()) {
|
|
56
|
+
const ext = extname(entry.name);
|
|
57
|
+
if (!ROUTE_EXTENSIONS.includes(ext)) continue;
|
|
58
|
+
const routePath = fileToRoutePath(fullPath, routesDir);
|
|
59
|
+
const { pattern, paramNames } = compilePattern(routePath);
|
|
60
|
+
results.push({
|
|
61
|
+
filePath: resolve(fullPath),
|
|
62
|
+
routePath,
|
|
63
|
+
paramNames,
|
|
64
|
+
pattern
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function scanServerRoutes(serverDir) {
|
|
70
|
+
const routesDir = join(serverDir, "routes");
|
|
71
|
+
if (!existsSync(routesDir) || !statSync(routesDir).isDirectory()) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const results = [];
|
|
75
|
+
scanDir(routesDir, routesDir, results);
|
|
76
|
+
results.sort((a, b) => {
|
|
77
|
+
if (a.paramNames.length === 0 && b.paramNames.length > 0) return -1;
|
|
78
|
+
if (a.paramNames.length > 0 && b.paramNames.length === 0) return 1;
|
|
79
|
+
return a.routePath.localeCompare(b.routePath);
|
|
80
|
+
});
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/server/action-scan.ts
|
|
85
|
+
import { readdirSync as readdirSync2, existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
86
|
+
import { join as join2, resolve as resolve2, relative as relative2, extname as extname2 } from "path";
|
|
87
|
+
var ACTION_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
88
|
+
function scanDir2(dir, actionsDir, results) {
|
|
89
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
const fullPath = join2(dir, entry.name);
|
|
92
|
+
if (entry.isDirectory()) {
|
|
93
|
+
if (!entry.name.startsWith("_") && !entry.name.startsWith(".")) {
|
|
94
|
+
scanDir2(fullPath, actionsDir, results);
|
|
95
|
+
}
|
|
96
|
+
} else if (entry.isFile()) {
|
|
97
|
+
const ext = extname2(entry.name);
|
|
98
|
+
if (!ACTION_EXTENSIONS.includes(ext)) continue;
|
|
99
|
+
let rel = relative2(actionsDir, fullPath);
|
|
100
|
+
rel = rel.replace(/\\/g, "/");
|
|
101
|
+
rel = rel.slice(0, -ext.length);
|
|
102
|
+
results.push({
|
|
103
|
+
filePath: resolve2(fullPath),
|
|
104
|
+
actionPath: rel
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function scanServerActions(serverDir) {
|
|
110
|
+
const actionsDir = join2(serverDir, "actions");
|
|
111
|
+
if (!existsSync2(actionsDir) || !statSync2(actionsDir).isDirectory()) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
const results = [];
|
|
115
|
+
scanDir2(actionsDir, actionsDir, results);
|
|
116
|
+
return results;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/server/ws-scan.ts
|
|
120
|
+
import { readdirSync as readdirSync3, existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
121
|
+
import { join as join3, resolve as resolve3, relative as relative3, extname as extname3 } from "path";
|
|
122
|
+
var WS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
123
|
+
function scanDir3(dir, wsDir, results) {
|
|
124
|
+
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
const fullPath = join3(dir, entry.name);
|
|
127
|
+
if (entry.isDirectory()) {
|
|
128
|
+
if (!entry.name.startsWith("_") && !entry.name.startsWith(".")) {
|
|
129
|
+
scanDir3(fullPath, wsDir, results);
|
|
130
|
+
}
|
|
131
|
+
} else if (entry.isFile()) {
|
|
132
|
+
const ext = extname3(entry.name);
|
|
133
|
+
if (!WS_EXTENSIONS.includes(ext)) continue;
|
|
134
|
+
let rel = relative3(wsDir, fullPath);
|
|
135
|
+
rel = rel.replace(/\\/g, "/");
|
|
136
|
+
rel = rel.slice(0, -ext.length);
|
|
137
|
+
if (rel.endsWith("/index")) rel = rel.slice(0, -6);
|
|
138
|
+
else if (rel === "index") rel = "";
|
|
139
|
+
results.push({
|
|
140
|
+
filePath: resolve3(fullPath),
|
|
141
|
+
wsPath: `/ws/${rel}`
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function scanWebSocketRoutes(serverDir) {
|
|
147
|
+
const wsDir = join3(serverDir, "ws");
|
|
148
|
+
if (!existsSync3(wsDir) || !statSync3(wsDir).isDirectory()) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const results = [];
|
|
152
|
+
scanDir3(wsDir, wsDir, results);
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
matchRoute,
|
|
158
|
+
scanServerRoutes,
|
|
159
|
+
scanServerActions,
|
|
160
|
+
scanWebSocketRoutes
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=chunk-U3OJFWK3.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import cac from "cac";
|
|
5
|
+
var cli = cac("theokit");
|
|
6
|
+
cli.command("dev", "Start development server").option("--port <port>", "Port number").action(async (options) => {
|
|
7
|
+
const { devCommand } = await import("../dev-7UJK3M2O.js");
|
|
8
|
+
await devCommand({ port: options.port ? Number(options.port) : void 0 });
|
|
9
|
+
});
|
|
10
|
+
cli.command("build", "Build for production").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
|
|
11
|
+
try {
|
|
12
|
+
const { buildCommand } = await import("../build-NSKFOAFX.js");
|
|
13
|
+
await buildCommand({ target: options.target });
|
|
14
|
+
} catch (err) {
|
|
15
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16
|
+
console.error(`
|
|
17
|
+
\u2717 ${msg}
|
|
18
|
+
`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
cli.command("start", "Start production server").option("--port <port>", "Port number").action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
const { startCommand } = await import("../start-BIS3RCFQ.js");
|
|
25
|
+
await startCommand({ port: options.port ? Number(options.port) : void 0 });
|
|
26
|
+
} catch (err) {
|
|
27
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28
|
+
console.error(`
|
|
29
|
+
\u2717 ${msg}
|
|
30
|
+
`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
cli.command("generate <type> <name>", "Generate a route, action, page, or ws endpoint").action(async (type, name) => {
|
|
35
|
+
try {
|
|
36
|
+
const { generateCommand } = await import("../generate-AA7ZE42F.js");
|
|
37
|
+
await generateCommand(type, name);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40
|
+
console.error(`
|
|
41
|
+
\u2717 ${msg}
|
|
42
|
+
`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
cli.command("routes", "List all routes, actions, and WebSocket endpoints").action(async () => {
|
|
47
|
+
try {
|
|
48
|
+
const { routesCommand } = await import("../routes-YP357VAC.js");
|
|
49
|
+
await routesCommand();
|
|
50
|
+
} catch (err) {
|
|
51
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
52
|
+
console.error(`
|
|
53
|
+
\u2717 ${msg}
|
|
54
|
+
`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
cli.command("docker", "Generate Dockerfile for production").option("--force", "Overwrite existing Dockerfile").action(async (options) => {
|
|
59
|
+
try {
|
|
60
|
+
const { dockerCommand } = await import("../docker-M253W54T.js");
|
|
61
|
+
await dockerCommand({ force: options.force });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
64
|
+
console.error(`
|
|
65
|
+
\u2717 ${msg}
|
|
66
|
+
`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
cli.help();
|
|
71
|
+
cli.version("0.1.0-alpha.0");
|
|
72
|
+
function main() {
|
|
73
|
+
cli.parse();
|
|
74
|
+
}
|
|
75
|
+
main();
|
|
76
|
+
export {
|
|
77
|
+
main
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":";;;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":[]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/** Infer the response type from a route's handler return */
|
|
4
|
+
type InferResponse<T> = T extends {
|
|
5
|
+
handler: (...args: never[]) => infer R;
|
|
6
|
+
} ? Awaited<R> : unknown;
|
|
7
|
+
/** Extract the query Zod schema type, handling optional properties */
|
|
8
|
+
type ExtractQuery<T> = T extends {
|
|
9
|
+
query?: infer Q;
|
|
10
|
+
} ? (Q extends z.ZodType ? Q : never) : never;
|
|
11
|
+
/** Extract the body Zod schema type, handling optional properties */
|
|
12
|
+
type ExtractBody<T> = T extends {
|
|
13
|
+
body?: infer B;
|
|
14
|
+
} ? (B extends z.ZodType ? B : never) : never;
|
|
15
|
+
/** Infer query type from a route's query Zod schema */
|
|
16
|
+
type InferQuery<T> = [ExtractQuery<T>] extends [never] ? undefined : ExtractQuery<T> extends z.ZodUndefined ? undefined : z.infer<ExtractQuery<T>>;
|
|
17
|
+
/** Infer body type from a route's body Zod schema */
|
|
18
|
+
type InferBody<T> = [ExtractBody<T>] extends [never] ? undefined : ExtractBody<T> extends z.ZodUndefined ? undefined : z.infer<ExtractBody<T>>;
|
|
19
|
+
/** Build the options type based on what schemas the route has */
|
|
20
|
+
type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> & (InferQuery<T> extends undefined ? {
|
|
21
|
+
query?: never;
|
|
22
|
+
} : {
|
|
23
|
+
query: InferQuery<T>;
|
|
24
|
+
}) & (InferBody<T> extends undefined ? {
|
|
25
|
+
body?: never;
|
|
26
|
+
} : {
|
|
27
|
+
body: InferBody<T>;
|
|
28
|
+
});
|
|
29
|
+
declare class TheoFetchError extends Error {
|
|
30
|
+
status: number;
|
|
31
|
+
code?: string;
|
|
32
|
+
issues?: unknown[];
|
|
33
|
+
constructor(status: number, body?: unknown);
|
|
34
|
+
}
|
|
35
|
+
declare function theoFetch<T>(url: string, options?: TheoFetchOptions<T>): Promise<InferResponse<T>>;
|
|
36
|
+
|
|
37
|
+
export { type InferBody, type InferQuery, type InferResponse, TheoFetchError, type TheoFetchOptions, theoFetch };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/client/theo-fetch.ts
|
|
2
|
+
var TheoFetchError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
issues;
|
|
6
|
+
constructor(status, body) {
|
|
7
|
+
const parsed = body && typeof body === "object" ? body : null;
|
|
8
|
+
const error = parsed?.error;
|
|
9
|
+
const message = error?.message ?? `HTTP ${status}`;
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "TheoFetchError";
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.code = error?.code;
|
|
14
|
+
this.issues = error?.issues;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
async function theoFetch(url, options) {
|
|
18
|
+
const fetchUrl = new URL(url, globalThis.location?.origin ?? "http://localhost:3000");
|
|
19
|
+
if (options && "query" in options && options.query) {
|
|
20
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
21
|
+
if (v !== void 0) {
|
|
22
|
+
fetchUrl.searchParams.set(k, String(v));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const { query: _q, body: _b, ...restOptions } = options ?? {};
|
|
27
|
+
const init = { ...restOptions };
|
|
28
|
+
if (options && "body" in options && options.body !== void 0) {
|
|
29
|
+
init.body = JSON.stringify(options.body);
|
|
30
|
+
init.headers = {
|
|
31
|
+
...init.headers ?? {},
|
|
32
|
+
"Content-Type": "application/json"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const response = await fetch(fetchUrl.toString(), init);
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
let errorBody;
|
|
38
|
+
try {
|
|
39
|
+
errorBody = await response.json();
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
throw new TheoFetchError(response.status, errorBody);
|
|
43
|
+
}
|
|
44
|
+
if (response.status === 204) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const contentLength = response.headers.get("content-length");
|
|
48
|
+
if (contentLength === "0") {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return response.json();
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
TheoFetchError,
|
|
55
|
+
theoFetch
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/theo-fetch.ts"],"sourcesContent":["import type { z } from 'zod'\n\n// --- Utility Types ---\n\n/** Infer the response type from a route's handler return */\nexport type InferResponse<T> = T extends { handler: (...args: never[]) => infer R }\n ? Awaited<R>\n : unknown\n\n/** Extract the query Zod schema type, handling optional properties */\ntype ExtractQuery<T> = T extends { query?: infer Q } ? (Q extends z.ZodType ? Q : never) : never\n\n/** Extract the body Zod schema type, handling optional properties */\ntype ExtractBody<T> = T extends { body?: infer B } ? (B extends z.ZodType ? B : never) : never\n\n/** Infer query type from a route's query Zod schema */\nexport type InferQuery<T> = [ExtractQuery<T>] extends [never]\n ? undefined\n : ExtractQuery<T> extends z.ZodUndefined ? undefined : z.infer<ExtractQuery<T>>\n\n/** Infer body type from a route's body Zod schema */\nexport type InferBody<T> = [ExtractBody<T>] extends [never]\n ? undefined\n : ExtractBody<T> extends z.ZodUndefined ? undefined : z.infer<ExtractBody<T>>\n\n/** Build the options type based on what schemas the route has */\nexport type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> &\n (InferQuery<T> extends undefined ? { query?: never } : { query: InferQuery<T> }) &\n (InferBody<T> extends undefined ? { body?: never } : { body: InferBody<T> })\n\n// --- Error Class ---\n\nexport class TheoFetchError extends Error {\n status: number\n code?: string\n issues?: unknown[]\n\n constructor(status: number, body?: unknown) {\n const parsed = body && typeof body === 'object' ? (body as Record<string, unknown>) : null\n const error = parsed?.error as Record<string, unknown> | undefined\n const message = (error?.message as string) ?? `HTTP ${status}`\n super(message)\n this.name = 'TheoFetchError'\n this.status = status\n this.code = error?.code as string | undefined\n this.issues = error?.issues as unknown[] | undefined\n }\n}\n\n// --- Main Function ---\n\nexport async function theoFetch<T>(\n url: string,\n options?: TheoFetchOptions<T>,\n): Promise<InferResponse<T>> {\n const fetchUrl = new URL(url, globalThis.location?.origin ?? 'http://localhost:3000')\n\n // Append query params (skip undefined values)\n if (options && 'query' in options && options.query) {\n for (const [k, v] of Object.entries(options.query as Record<string, unknown>)) {\n if (v !== undefined) {\n fetchUrl.searchParams.set(k, String(v))\n }\n }\n }\n\n // Build fetch init\n const { query: _q, body: _b, ...restOptions } = (options ?? {}) as Record<string, unknown>\n const init: RequestInit = { ...(restOptions as RequestInit) }\n\n if (options && 'body' in options && options.body !== undefined) {\n init.body = JSON.stringify(options.body)\n init.headers = {\n ...(init.headers as Record<string, string> ?? {}),\n 'Content-Type': 'application/json',\n }\n }\n\n const response = await fetch(fetchUrl.toString(), init)\n\n if (!response.ok) {\n let errorBody: unknown\n try {\n errorBody = await response.json()\n } catch {\n // Non-JSON error response\n }\n throw new TheoFetchError(response.status, errorBody)\n }\n\n // Handle 204 No Content (EC-1)\n if (response.status === 204) {\n return null as InferResponse<T>\n }\n\n // Check for empty body\n const contentLength = response.headers.get('content-length')\n if (contentLength === '0') {\n return null as InferResponse<T>\n }\n\n return response.json() as Promise<InferResponse<T>>\n}\n"],"mappings":";AAgCO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,QAAgB,MAAgB;AAC1C,UAAM,SAAS,QAAQ,OAAO,SAAS,WAAY,OAAmC;AACtF,UAAM,QAAQ,QAAQ;AACtB,UAAM,UAAW,OAAO,WAAsB,QAAQ,MAAM;AAC5D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO,OAAO;AACnB,SAAK,SAAS,OAAO;AAAA,EACvB;AACF;AAIA,eAAsB,UACpB,KACA,SAC2B;AAC3B,QAAM,WAAW,IAAI,IAAI,KAAK,WAAW,UAAU,UAAU,uBAAuB;AAGpF,MAAI,WAAW,WAAW,WAAW,QAAQ,OAAO;AAClD,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,KAAgC,GAAG;AAC7E,UAAI,MAAM,QAAW;AACnB,iBAAS,aAAa,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,EAAE,OAAO,IAAI,MAAM,IAAI,GAAG,YAAY,IAAK,WAAW,CAAC;AAC7D,QAAM,OAAoB,EAAE,GAAI,YAA4B;AAE5D,MAAI,WAAW,UAAU,WAAW,QAAQ,SAAS,QAAW;AAC9D,SAAK,OAAO,KAAK,UAAU,QAAQ,IAAI;AACvC,SAAK,UAAU;AAAA,MACb,GAAI,KAAK,WAAqC,CAAC;AAAA,MAC/C,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,MAAM,SAAS,SAAS,GAAG,IAAI;AAEtD,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI;AACJ,QAAI;AACF,kBAAY,MAAM,SAAS,KAAK;AAAA,IAClC,QAAQ;AAAA,IAER;AACA,UAAM,IAAI,eAAe,SAAS,QAAQ,SAAS;AAAA,EACrD;AAGA,MAAI,SAAS,WAAW,KAAK;AAC3B,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;AAC3D,MAAI,kBAAkB,KAAK;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,SAAS,KAAK;AACvB;","names":[]}
|