theokit 0.5.3 → 0.6.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.
Files changed (114) hide show
  1. package/dist/{aws-lambda-SCRGTGF5.js → aws-lambda-WT4HRBNL.js} +3 -3
  2. package/dist/{build-R74EU6BP.js → build-ZZAQV2ES.js} +7 -7
  3. package/dist/{bun-FURRCQMS.js → bun-UTDVN6R4.js} +3 -3
  4. package/dist/{check-NXYPLM6M.js → check-F3UDYSZ4.js} +2 -2
  5. package/dist/{chunk-KJLECZWQ.js → chunk-3FYJ7R7N.js} +57 -14
  6. package/dist/chunk-3FYJ7R7N.js.map +1 -0
  7. package/dist/{chunk-JD7AQNJS.js → chunk-4NLIESWK.js} +18 -12
  8. package/dist/{chunk-JD7AQNJS.js.map → chunk-4NLIESWK.js.map} +1 -1
  9. package/dist/{chunk-H4J2LECY.js → chunk-6BUUZ2PK.js} +9 -9
  10. package/dist/chunk-6BUUZ2PK.js.map +1 -0
  11. package/dist/{chunk-FGOX37NB.js → chunk-BNGBG2SQ.js} +21 -2
  12. package/dist/chunk-BNGBG2SQ.js.map +1 -0
  13. package/dist/{chunk-X32KQH5C.js → chunk-CCVC6P6B.js} +3 -3
  14. package/dist/chunk-CCVC6P6B.js.map +1 -0
  15. package/dist/{chunk-S74FECKW.js → chunk-D4SV7JOG.js} +24 -20
  16. package/dist/chunk-D4SV7JOG.js.map +1 -0
  17. package/dist/{chunk-DNMSV3R3.js → chunk-D7ZH7DTG.js} +3 -3
  18. package/dist/chunk-D7ZH7DTG.js.map +1 -0
  19. package/dist/{chunk-MWYSRZI4.js → chunk-EQV67HZL.js} +2 -2
  20. package/dist/{chunk-CG563V5M.js → chunk-IEES3CHD.js} +39 -5
  21. package/dist/chunk-IEES3CHD.js.map +1 -0
  22. package/dist/{chunk-DUKXQNM7.js → chunk-IHMJV2OK.js} +2 -2
  23. package/dist/{chunk-WGHJD6I7.js → chunk-JAIKGP3Q.js} +76 -21
  24. package/dist/chunk-JAIKGP3Q.js.map +1 -0
  25. package/dist/{chunk-NZXH2LQQ.js → chunk-SJIRP73B.js} +18 -1
  26. package/dist/chunk-SJIRP73B.js.map +1 -0
  27. package/dist/{chunk-X3VNROZ7.js → chunk-SVSUUK4H.js} +73 -14
  28. package/dist/chunk-SVSUUK4H.js.map +1 -0
  29. package/dist/cli/index.js +25 -10
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/{cloudflare-IZ6VJ2OU.js → cloudflare-U5GYYI47.js} +3 -3
  32. package/dist/db-ZPDW3UCU.js +78 -0
  33. package/dist/db-ZPDW3UCU.js.map +1 -0
  34. package/dist/{deno-deploy-O73NBXIW.js → deno-deploy-3QHJ3AJQ.js} +3 -3
  35. package/dist/{dev-ZU46C5AZ.js → dev-MHCQ5RD7.js} +16 -8
  36. package/dist/dev-MHCQ5RD7.js.map +1 -0
  37. package/dist/{dev-emit-66FCOS7V.js → dev-emit-4WG3B5BM.js} +2 -2
  38. package/dist/{dev-emit-JBVINGHU.js → dev-emit-6DJUK6DT.js} +76 -21
  39. package/dist/dev-emit-6DJUK6DT.js.map +1 -0
  40. package/dist/devtools/entry.js +2 -3
  41. package/dist/devtools/entry.js.map +1 -1
  42. package/dist/generate-ODDAYXNR.js +494 -0
  43. package/dist/generate-ODDAYXNR.js.map +1 -0
  44. package/dist/index.d.ts +32 -3
  45. package/dist/index.js +3 -4
  46. package/dist/index.js.map +1 -1
  47. package/dist/{info-CORLCPEJ.js → info-CSGWIAXA.js} +5 -5
  48. package/dist/{netlify-O7G3RLK4.js → netlify-DUB7BZ4E.js} +3 -3
  49. package/dist/{node-LXPQLMVB.js → node-ELE236WK.js} +3 -3
  50. package/dist/{openapi-TEOUOIJ2.js → openapi-KSY272CW.js} +5 -5
  51. package/dist/registry-B7FA6KMI.js +25 -0
  52. package/dist/{routes-GAXVWJUK.js → routes-NWJA5L5I.js} +4 -4
  53. package/dist/{scan-NQFI5S7P.js → scan-I5PT6TUX.js} +2 -2
  54. package/dist/{schema-D3v8VB7o.d.ts → schema-BpH6ivDY.d.ts} +2 -4
  55. package/dist/{schema-Z5VJ2I76.js → schema-NPI4NJEF.js} +3 -3
  56. package/dist/server/auth/index.js +3 -5
  57. package/dist/server/define/index.d.ts +7 -0
  58. package/dist/server/define/index.js +1 -1
  59. package/dist/server/http/index.js +1 -2
  60. package/dist/server/index.d.ts +34 -1
  61. package/dist/server/index.js +57 -48
  62. package/dist/server/index.js.map +1 -1
  63. package/dist/{services-typed-client-ASZL7FQV.js → services-typed-client-24VBMDOF.js} +2 -2
  64. package/dist/{services-typed-client-KK6RHRDG.js → services-typed-client-IETY2SAK.js} +2 -2
  65. package/dist/{start-HGUNNF5X.js → start-OAAAQ7Z4.js} +84 -69
  66. package/dist/start-OAAAQ7Z4.js.map +1 -0
  67. package/dist/{static-EO73I4C7.js → static-YWF2M6YT.js} +4 -4
  68. package/dist/{theo-cloud-HBCYEODQ.js → theo-cloud-EUK56I7O.js} +2 -2
  69. package/dist/{vercel-GSFDMF7K.js → vercel-LGDQWYZD.js} +3 -3
  70. package/dist/vite-plugin/index.d.ts +1 -1
  71. package/dist/vite-plugin/index.js +3 -4
  72. package/dist/{vite-plugin-TX5H4MB6.js → vite-plugin-IF2O6BJV.js} +7 -7
  73. package/package.json +4 -1
  74. package/dist/chunk-4HBI7DHP.js +0 -20
  75. package/dist/chunk-4HBI7DHP.js.map +0 -1
  76. package/dist/chunk-CG563V5M.js.map +0 -1
  77. package/dist/chunk-DNMSV3R3.js.map +0 -1
  78. package/dist/chunk-FGOX37NB.js.map +0 -1
  79. package/dist/chunk-H4J2LECY.js.map +0 -1
  80. package/dist/chunk-KJLECZWQ.js.map +0 -1
  81. package/dist/chunk-NZXH2LQQ.js.map +0 -1
  82. package/dist/chunk-S74FECKW.js.map +0 -1
  83. package/dist/chunk-WGHJD6I7.js.map +0 -1
  84. package/dist/chunk-X32KQH5C.js.map +0 -1
  85. package/dist/chunk-X3VNROZ7.js.map +0 -1
  86. package/dist/dev-ZU46C5AZ.js.map +0 -1
  87. package/dist/dev-emit-JBVINGHU.js.map +0 -1
  88. package/dist/generate-AZ2K6Z2Z.js +0 -281
  89. package/dist/generate-AZ2K6Z2Z.js.map +0 -1
  90. package/dist/registry-KW4F4D2L.js +0 -25
  91. package/dist/start-HGUNNF5X.js.map +0 -1
  92. /package/dist/{aws-lambda-SCRGTGF5.js.map → aws-lambda-WT4HRBNL.js.map} +0 -0
  93. /package/dist/{build-R74EU6BP.js.map → build-ZZAQV2ES.js.map} +0 -0
  94. /package/dist/{bun-FURRCQMS.js.map → bun-UTDVN6R4.js.map} +0 -0
  95. /package/dist/{check-NXYPLM6M.js.map → check-F3UDYSZ4.js.map} +0 -0
  96. /package/dist/{chunk-MWYSRZI4.js.map → chunk-EQV67HZL.js.map} +0 -0
  97. /package/dist/{chunk-DUKXQNM7.js.map → chunk-IHMJV2OK.js.map} +0 -0
  98. /package/dist/{cloudflare-IZ6VJ2OU.js.map → cloudflare-U5GYYI47.js.map} +0 -0
  99. /package/dist/{deno-deploy-O73NBXIW.js.map → deno-deploy-3QHJ3AJQ.js.map} +0 -0
  100. /package/dist/{dev-emit-66FCOS7V.js.map → dev-emit-4WG3B5BM.js.map} +0 -0
  101. /package/dist/{info-CORLCPEJ.js.map → info-CSGWIAXA.js.map} +0 -0
  102. /package/dist/{netlify-O7G3RLK4.js.map → netlify-DUB7BZ4E.js.map} +0 -0
  103. /package/dist/{node-LXPQLMVB.js.map → node-ELE236WK.js.map} +0 -0
  104. /package/dist/{openapi-TEOUOIJ2.js.map → openapi-KSY272CW.js.map} +0 -0
  105. /package/dist/{registry-KW4F4D2L.js.map → registry-B7FA6KMI.js.map} +0 -0
  106. /package/dist/{routes-GAXVWJUK.js.map → routes-NWJA5L5I.js.map} +0 -0
  107. /package/dist/{scan-NQFI5S7P.js.map → scan-I5PT6TUX.js.map} +0 -0
  108. /package/dist/{schema-Z5VJ2I76.js.map → schema-NPI4NJEF.js.map} +0 -0
  109. /package/dist/{services-typed-client-ASZL7FQV.js.map → services-typed-client-24VBMDOF.js.map} +0 -0
  110. /package/dist/{services-typed-client-KK6RHRDG.js.map → services-typed-client-IETY2SAK.js.map} +0 -0
  111. /package/dist/{static-EO73I4C7.js.map → static-YWF2M6YT.js.map} +0 -0
  112. /package/dist/{theo-cloud-HBCYEODQ.js.map → theo-cloud-EUK56I7O.js.map} +0 -0
  113. /package/dist/{vercel-GSFDMF7K.js.map → vercel-LGDQWYZD.js.map} +0 -0
  114. /package/dist/{vite-plugin-TX5H4MB6.js.map → vite-plugin-IF2O6BJV.js.map} +0 -0
@@ -0,0 +1,494 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+ import "./chunk-KT3MWNZG.js";
4
+
5
+ // src/cli/commands/generate.ts
6
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
7
+ import { resolve as resolve2, dirname as dirname2 } from "path";
8
+
9
+ // src/cli/commands/generate-resource.ts
10
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
11
+ import { resolve, dirname } from "path";
12
+ var ALLOWED_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "text", "number", "boolean"]);
13
+ var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "created_at"]);
14
+ function parseResourceFields(args) {
15
+ if (args.length === 0) {
16
+ throw new Error(
17
+ "Resource requires at least one field. Example: theokit generate resource posts title:string"
18
+ );
19
+ }
20
+ return args.map((arg) => {
21
+ const parts = arg.split(":");
22
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
23
+ throw new Error(`Invalid field "${arg}". Use name:type format (e.g. title:string)`);
24
+ }
25
+ const [name, type] = parts;
26
+ if (!ALLOWED_FIELD_TYPES.has(type)) {
27
+ throw new Error(
28
+ `Unknown field type "${type}" in "${arg}". Valid types: ${[...ALLOWED_FIELD_TYPES].join(", ")}`
29
+ );
30
+ }
31
+ if (RESERVED_FIELDS.has(name)) {
32
+ throw new Error(`Field "${name}" is reserved (auto-generated by the framework)`);
33
+ }
34
+ const fieldType = type;
35
+ return {
36
+ name,
37
+ type: fieldType,
38
+ drizzleColumn: mapDrizzleColumn(name, fieldType),
39
+ zodType: mapZodType(fieldType)
40
+ };
41
+ });
42
+ }
43
+ function mapDrizzleColumn(name, type) {
44
+ switch (type) {
45
+ case "string":
46
+ case "text":
47
+ return `text('${name}').notNull()`;
48
+ case "number":
49
+ return `integer('${name}').notNull()`;
50
+ case "boolean":
51
+ return `integer('${name}', { mode: 'boolean' }).notNull().default(false)`;
52
+ }
53
+ }
54
+ function mapZodType(type) {
55
+ switch (type) {
56
+ case "string":
57
+ case "text":
58
+ return "z.string()";
59
+ case "number":
60
+ return "z.number()";
61
+ case "boolean":
62
+ return "z.boolean()";
63
+ }
64
+ }
65
+ function generateSchemaEntry(resourceName, fields) {
66
+ const cols = [
67
+ ` id: integer('id').primaryKey({ autoIncrement: true }),`,
68
+ ...fields.map((f) => ` ${f.name}: ${f.drizzleColumn},`),
69
+ ` createdAt: text('created_at')`,
70
+ ` .notNull()`,
71
+ ` .$defaultFn(() => new Date().toISOString().split('T')[0]),`
72
+ ];
73
+ return [
74
+ ``,
75
+ `export const ${resourceName} = sqliteTable('${resourceName}', {`,
76
+ ...cols,
77
+ `})`,
78
+ ``
79
+ ].join("\n");
80
+ }
81
+ function generateRouteIndex(resourceName, fields) {
82
+ const bodyFields = fields.map((f) => ` ${f.name}: ${f.zodType},`).join("\n");
83
+ return [
84
+ `import { defineRoute } from 'theokit/server/define'`,
85
+ `import { z } from 'zod'`,
86
+ `import { db } from '../../db/index.js'`,
87
+ `import { ${resourceName} } from '../../db/schema.js'`,
88
+ ``,
89
+ `export const GET = defineRoute({`,
90
+ ` handler: () => db.select().from(${resourceName}).all(),`,
91
+ `})`,
92
+ ``,
93
+ `export const POST = defineRoute({`,
94
+ ` body: z.object({`,
95
+ bodyFields,
96
+ ` }),`,
97
+ ` status: 201,`,
98
+ ` handler: ({ body }) => {`,
99
+ ` const result = db.insert(${resourceName}).values(body).returning().get()`,
100
+ ` return result`,
101
+ ` },`,
102
+ `})`,
103
+ ``
104
+ ].join("\n");
105
+ }
106
+ function generateRouteId(resourceName, fields) {
107
+ const updateFields = fields.map((f) => ` ${f.name}: ${f.zodType}.optional(),`).join("\n");
108
+ return [
109
+ `import { defineRoute } from 'theokit/server/define'`,
110
+ `import { z } from 'zod'`,
111
+ `import { db } from '../../db/index.js'`,
112
+ `import { ${resourceName} } from '../../db/schema.js'`,
113
+ `import { eq } from 'drizzle-orm'`,
114
+ ``,
115
+ `export const GET = defineRoute({`,
116
+ ` params: z.object({ id: z.coerce.number() }),`,
117
+ ` handler: ({ params }) => {`,
118
+ ` const item = db.select().from(${resourceName}).where(eq(${resourceName}.id, params.id)).get()`,
119
+ ` if (!item) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,
120
+ ` return item`,
121
+ ` },`,
122
+ `})`,
123
+ ``,
124
+ `export const PUT = defineRoute({`,
125
+ ` params: z.object({ id: z.coerce.number() }),`,
126
+ ` body: z.object({`,
127
+ updateFields,
128
+ ` }),`,
129
+ ` handler: ({ params, body }) => {`,
130
+ ` const result = db.update(${resourceName}).set(body).where(eq(${resourceName}.id, params.id)).returning().get()`,
131
+ ` if (!result) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,
132
+ ` return result`,
133
+ ` },`,
134
+ `})`,
135
+ ``,
136
+ `export const DELETE = defineRoute({`,
137
+ ` params: z.object({ id: z.coerce.number() }),`,
138
+ ` status: 204,`,
139
+ ` handler: ({ params }) => {`,
140
+ ` db.delete(${resourceName}).where(eq(${resourceName}.id, params.id)).run()`,
141
+ ` },`,
142
+ `})`,
143
+ ``
144
+ ].join("\n");
145
+ }
146
+ function generateTestTemplate(resourceName) {
147
+ return [
148
+ `import { describe, it, expect } from 'vitest'`,
149
+ ``,
150
+ `describe('${resourceName} API', () => {`,
151
+ ` it('should have a schema defined', async () => {`,
152
+ ` const schema = await import('../server/db/schema.js')`,
153
+ ` expect(schema.${resourceName}).toBeDefined()`,
154
+ ` })`,
155
+ ``,
156
+ ` it('should have route handlers', async () => {`,
157
+ ` const index = await import('../server/routes/${resourceName}/index.js')`,
158
+ ` expect(index.GET).toBeDefined()`,
159
+ ` expect(index.POST).toBeDefined()`,
160
+ ` })`,
161
+ `})`,
162
+ ``
163
+ ].join("\n");
164
+ }
165
+ function generateResource(cwd, name, fieldArgs) {
166
+ let fields;
167
+ try {
168
+ fields = parseResourceFields(fieldArgs);
169
+ } catch (err) {
170
+ return { status: "invalid_name", message: err instanceof Error ? err.message : String(err) };
171
+ }
172
+ const schemaPath = resolve(cwd, "server/db/schema.ts");
173
+ const routeIndexPath = resolve(cwd, `server/routes/${name}/index.ts`);
174
+ const routeIdPath = resolve(cwd, `server/routes/${name}/[id].ts`);
175
+ const testPath = resolve(cwd, `tests/${name}.test.ts`);
176
+ if (!existsSync(schemaPath)) {
177
+ return {
178
+ status: "not_a_project",
179
+ message: "server/db/schema.ts not found. Run this from a TheoKit project with database setup."
180
+ };
181
+ }
182
+ if (existsSync(routeIndexPath)) {
183
+ return { status: "already_exists", filePath: routeIndexPath, kind: "resource", name };
184
+ }
185
+ const existingSchema = readFileSync(schemaPath, "utf-8");
186
+ if (existingSchema.includes(`export const ${name} =`)) {
187
+ return {
188
+ status: "already_exists",
189
+ filePath: schemaPath,
190
+ kind: "resource",
191
+ name,
192
+ message: `Table "${name}" already exists in schema.ts`
193
+ };
194
+ }
195
+ appendFileSync(schemaPath, generateSchemaEntry(name, fields));
196
+ mkdirSync(dirname(routeIndexPath), { recursive: true });
197
+ writeFileSync(routeIndexPath, generateRouteIndex(name, fields));
198
+ writeFileSync(routeIdPath, generateRouteId(name, fields));
199
+ mkdirSync(dirname(testPath), { recursive: true });
200
+ if (!existsSync(testPath)) {
201
+ writeFileSync(testPath, generateTestTemplate(name));
202
+ }
203
+ return { status: "created", filePath: routeIndexPath, kind: "resource", name };
204
+ }
205
+
206
+ // src/cli/commands/generate-types.ts
207
+ var VALID_TYPES = [
208
+ "route",
209
+ "action",
210
+ "page",
211
+ "ws",
212
+ "controller",
213
+ "agent",
214
+ "toolbox",
215
+ "resource"
216
+ ];
217
+
218
+ // src/cli/commands/generate.ts
219
+ function toKebabCase(name) {
220
+ return /^[a-z][a-z0-9/-]*$/.test(name);
221
+ }
222
+ var RESERVED_BASENAMES = /* @__PURE__ */ new Set([
223
+ "index",
224
+ "constructor",
225
+ "__proto__",
226
+ "prototype",
227
+ "hasOwnProperty"
228
+ ]);
229
+ function hasReservedSegment(name) {
230
+ const basename = name.includes("/") ? name.split("/").pop() ?? name : name;
231
+ return RESERVED_BASENAMES.has(basename);
232
+ }
233
+ function isPathInside(cwd, targetSubpath) {
234
+ if (targetSubpath.includes("\0")) return false;
235
+ if (targetSubpath.startsWith("/") || targetSubpath.startsWith("\\")) return false;
236
+ const resolved = resolve2(cwd, targetSubpath);
237
+ const cwdResolved = resolve2(cwd);
238
+ const sep = process.platform === "win32" ? "\\" : "/";
239
+ return resolved === cwdResolved || resolved.startsWith(cwdResolved + sep);
240
+ }
241
+ function toPascalCase(name) {
242
+ return name.split(/[-/]/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
243
+ }
244
+ function toCamelCase(name) {
245
+ const pascal = toPascalCase(name);
246
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
247
+ }
248
+ function generateRouteTemplate(name) {
249
+ return [
250
+ `import { defineRoute } from 'theokit/server'`,
251
+ `import { z } from 'zod'`,
252
+ ``,
253
+ `export const GET = defineRoute({`,
254
+ ` handler: ({ ctx }) => {`,
255
+ ` return { message: 'TODO: implement ${name} GET' }`,
256
+ ` },`,
257
+ `})`,
258
+ ``
259
+ ].join("\n");
260
+ }
261
+ function generateControllerTemplate(name) {
262
+ const className = toPascalCase(name) + "Controller";
263
+ const baseName = name.split("/").pop() ?? name;
264
+ return [
265
+ `// AUTO-GENERATED by \`theokit generate controller ${name}\``,
266
+ `import { Controller, Get } from '@theokit/http'`,
267
+ ``,
268
+ `@Controller('${baseName}')`,
269
+ `export class ${className} {`,
270
+ ` @Get()`,
271
+ ` findAll(): string {`,
272
+ ` return 'This action returns all ${baseName}'`,
273
+ ` }`,
274
+ `}`,
275
+ ``
276
+ ].join("\n");
277
+ }
278
+ function generateActionTemplate(name) {
279
+ const camel = toCamelCase(name);
280
+ return [
281
+ `import { defineAction } from 'theokit/server'`,
282
+ `import { z } from 'zod'`,
283
+ ``,
284
+ `export const ${camel} = defineAction({`,
285
+ ` input: z.object({}),`,
286
+ ` handler: ({ input, ctx }) => {`,
287
+ ` return { message: 'TODO: implement ${name}' }`,
288
+ ` },`,
289
+ `})`,
290
+ ``
291
+ ].join("\n");
292
+ }
293
+ function generateAgentTemplate(name) {
294
+ const pascal = toPascalCase(name);
295
+ return [
296
+ `import 'reflect-metadata'`,
297
+ `import { Agent, MainLoop } from '@theokit/agents'`,
298
+ `import { UseGuards } from '@theokit/http'`,
299
+ ``,
300
+ `@Agent({`,
301
+ ` name: '${name}',`,
302
+ ` route: '/api/agents/${name}',`,
303
+ ` model: 'openai/gpt-4o-mini',`,
304
+ ` systemPrompt: 'You are a helpful ${name} assistant.',`,
305
+ `})`,
306
+ `export class ${pascal}Agent {`,
307
+ ` @MainLoop({ strategy: 'react', maxIterations: 5 })`,
308
+ ` async run() {}`,
309
+ `}`,
310
+ ``
311
+ ].join("\n");
312
+ }
313
+ function generateToolboxTemplate(name) {
314
+ const pascal = toPascalCase(name);
315
+ return [
316
+ `import 'reflect-metadata'`,
317
+ `import { z } from 'zod'`,
318
+ `import { Toolbox, Tool } from '@theokit/agents'`,
319
+ ``,
320
+ `@Toolbox({ namespace: '${name}' })`,
321
+ `export class ${pascal}Tools {`,
322
+ ` @Tool({`,
323
+ ` name: 'hello',`,
324
+ ` description: 'Say hello',`,
325
+ ` input: z.object({ name: z.string() }),`,
326
+ ` })`,
327
+ ` async hello(input: { name: string }) {`,
328
+ ` return \`Hello, \${input.name}!\``,
329
+ ` }`,
330
+ `}`,
331
+ ``
332
+ ].join("\n");
333
+ }
334
+ function resolveTemplate(cwd, type, name) {
335
+ switch (type) {
336
+ case "route":
337
+ return {
338
+ filePath: resolve2(cwd, "server/routes", `${name}.ts`),
339
+ content: generateRouteTemplate(name)
340
+ };
341
+ case "action":
342
+ return {
343
+ filePath: resolve2(cwd, "server/actions", `${name}.ts`),
344
+ content: generateActionTemplate(name)
345
+ };
346
+ case "page":
347
+ return { filePath: resolve2(cwd, `app/${name}/page.tsx`), content: generatePageTemplate(name) };
348
+ case "ws":
349
+ return {
350
+ filePath: resolve2(cwd, "server/ws", `${name}.ts`),
351
+ content: generateWsTemplate(name)
352
+ };
353
+ case "controller":
354
+ return {
355
+ filePath: resolve2(cwd, "server/controllers", `${name}.controller.ts`),
356
+ content: generateControllerTemplate(name)
357
+ };
358
+ case "agent":
359
+ return {
360
+ filePath: resolve2(cwd, "server/agents", `${name}.agent.ts`),
361
+ content: generateAgentTemplate(name)
362
+ };
363
+ case "toolbox":
364
+ return {
365
+ filePath: resolve2(cwd, "server/toolboxes", `${name}.tools.ts`),
366
+ content: generateToolboxTemplate(name)
367
+ };
368
+ default:
369
+ return null;
370
+ }
371
+ }
372
+ function generateActionTestTemplate(name) {
373
+ const camel = toCamelCase(name);
374
+ return [
375
+ `import { describe, it, expect } from 'vitest'`,
376
+ ``,
377
+ `import { ${camel} } from './${name.split("/").pop()}.js'`,
378
+ ``,
379
+ `describe('${name} action', () => {`,
380
+ ` it('should accept a valid input shape', () => {`,
381
+ ` expect(${camel}.input).toBeDefined()`,
382
+ ` expect(typeof ${camel}.handler).toBe('function')`,
383
+ ` })`,
384
+ ``,
385
+ ` it('should reject invalid input via zod schema', () => {`,
386
+ ` const parsed = ${camel}.input.safeParse({ __invalid: true })`,
387
+ ` // Empty object schema accepts {}; tighten this assertion when adding fields.`,
388
+ ` expect(parsed.success).toBe(true)`,
389
+ ` })`,
390
+ `})`,
391
+ ``
392
+ ].join("\n");
393
+ }
394
+ function generatePageTemplate(name) {
395
+ const pascal = toPascalCase(name);
396
+ return [`export default function ${pascal}Page() {`, ` return <h1>${pascal}</h1>`, `}`, ``].join(
397
+ "\n"
398
+ );
399
+ }
400
+ function generateWsTemplate(_name) {
401
+ return [
402
+ `import { defineWebSocket } from 'theokit/server'`,
403
+ ``,
404
+ `export default defineWebSocket({`,
405
+ ` onMessage(ws, data) {`,
406
+ ` ws.send(\`echo: \${data}\`)`,
407
+ ` },`,
408
+ `})`,
409
+ ``
410
+ ].join("\n");
411
+ }
412
+ async function generate(opts) {
413
+ const { cwd, type, name } = opts;
414
+ if (!existsSync2(resolve2(cwd, "theo.config.ts")) && !existsSync2(resolve2(cwd, "theo.config.js"))) {
415
+ return {
416
+ status: "not_a_project",
417
+ message: "Not a Theo project. cwd has no theo.config.ts or theo.config.js"
418
+ };
419
+ }
420
+ if (!VALID_TYPES.includes(type)) {
421
+ return {
422
+ status: "invalid_kind",
423
+ message: `Invalid generator type "${type}". Available: ${VALID_TYPES.join(", ")}`
424
+ };
425
+ }
426
+ if (!name || !toKebabCase(name)) {
427
+ return {
428
+ status: "invalid_name",
429
+ message: `Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens.`
430
+ };
431
+ }
432
+ if (hasReservedSegment(name)) {
433
+ return {
434
+ status: "invalid_name",
435
+ message: `Reserved name "${name}" \u2014 basename collides with built-in identifier (index/constructor/__proto__/prototype/hasOwnProperty).`
436
+ };
437
+ }
438
+ if (type === "resource") {
439
+ return generateResource(cwd, name, opts.fields ?? []);
440
+ }
441
+ const resolved = resolveTemplate(cwd, type, name);
442
+ if (resolved === null) {
443
+ return { status: "invalid_kind", message: `Unknown type: ${type}` };
444
+ }
445
+ const { filePath, content } = resolved;
446
+ const relativeFromCwd = filePath.startsWith(resolve2(cwd)) ? filePath.slice(resolve2(cwd).length + 1) : filePath;
447
+ if (!isPathInside(cwd, relativeFromCwd)) {
448
+ return {
449
+ status: "invalid_name",
450
+ message: `Path traversal denied: "${name}" would escape project root.`
451
+ };
452
+ }
453
+ if (existsSync2(filePath)) {
454
+ return { status: "already_exists", filePath, kind: type, name };
455
+ }
456
+ mkdirSync2(dirname2(filePath), { recursive: true });
457
+ writeFileSync2(filePath, content);
458
+ if (type === "action") {
459
+ const testPath = filePath.replace(/\.ts$/, ".test.ts");
460
+ if (!existsSync2(testPath)) {
461
+ writeFileSync2(testPath, generateActionTestTemplate(name));
462
+ }
463
+ }
464
+ return { status: "created", filePath, kind: type, name };
465
+ }
466
+ async function generateCommand(type, name, fields) {
467
+ const result = await generate({ cwd: process.cwd(), type, name, fields });
468
+ switch (result.status) {
469
+ case "not_a_project":
470
+ throw new Error("Not a Theo project. Run this from a project root with theo.config.ts");
471
+ case "invalid_kind":
472
+ throw new Error(result.message ?? "Invalid kind");
473
+ case "invalid_name":
474
+ throw new Error(
475
+ `Invalid name "${name}". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`
476
+ );
477
+ case "already_exists":
478
+ console.log(`
479
+ \u26A0 ${result.filePath} already exists. Skipping.
480
+ `);
481
+ return;
482
+ case "created":
483
+ console.log(`
484
+ \u2713 Created ${type}: ${result.filePath}
485
+ `);
486
+ return;
487
+ }
488
+ }
489
+ export {
490
+ VALID_TYPES,
491
+ generate,
492
+ generateCommand
493
+ };
494
+ //# sourceMappingURL=generate-ODDAYXNR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/generate.ts","../src/cli/commands/generate-resource.ts","../src/cli/commands/generate-types.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\n\nimport { generateResource } from './generate-resource.js'\nimport {\n VALID_TYPES,\n type GeneratorType,\n type GenerateOptions,\n type GenerateStatus,\n type GenerateResult,\n} from './generate-types.js'\n\nexport {\n VALID_TYPES,\n type GeneratorType,\n type GenerateOptions,\n type GenerateStatus,\n type GenerateResult,\n}\n\nfunction toKebabCase(name: string): boolean {\n return /^[a-z][a-z0-9/-]*$/.test(name)\n}\n\n/**\n * Reserved JS identifier names that conflict with object prototype machinery\n * or would shadow built-in module shapes if used as action/route names.\n * Mirrors action-scan's RESERVED_NAMES (T1.4).\n */\nconst RESERVED_BASENAMES = new Set([\n 'index',\n 'constructor',\n '__proto__',\n 'prototype',\n 'hasOwnProperty',\n])\n\n/**\n * Validate that a name segment doesn't hit the reserved-identifier list.\n * Checks the BASENAME (last `/` segment) — nested `admin/constructor` still\n * rejected because the file `constructor.ts` would be the conflict.\n */\nfunction hasReservedSegment(name: string): boolean {\n const basename = name.includes('/') ? (name.split('/').pop() ?? name) : name\n return RESERVED_BASENAMES.has(basename)\n}\n\n/**\n * EC-4: validate that resolving `targetSubpath` under `cwd` stays inside `cwd`.\n * Returns `true` when path is safe (stays inside), `false` when it escapes via\n * `..`, absolute path, null byte, or similar traversal vector.\n */\nfunction isPathInside(cwd: string, targetSubpath: string): boolean {\n if (targetSubpath.includes('\\x00')) return false\n if (targetSubpath.startsWith('/') || targetSubpath.startsWith('\\\\')) return false\n const resolved = resolve(cwd, targetSubpath)\n const cwdResolved = resolve(cwd)\n const sep = process.platform === 'win32' ? '\\\\' : '/'\n return resolved === cwdResolved || resolved.startsWith(cwdResolved + sep)\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 generateControllerTemplate(name: string): string {\n const className = toPascalCase(name) + 'Controller'\n const baseName = name.split('/').pop() ?? name\n return [\n `// AUTO-GENERATED by \\`theokit generate controller ${name}\\``,\n `import { Controller, Get } from '@theokit/http'`,\n ``,\n `@Controller('${baseName}')`,\n `export class ${className} {`,\n ` @Get()`,\n ` findAll(): string {`,\n ` return 'This action returns all ${baseName}'`,\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\n/**\n * Co-located test template for `theokit generate action <name>` (T5.2 +\n * plan Q4 cenário 1: roundtrip serialize). Skeleton uses vitest BDD shape\n * per testing.md.\n */\nfunction generateAgentTemplate(name: string): string {\n const pascal = toPascalCase(name)\n return [\n `import 'reflect-metadata'`,\n `import { Agent, MainLoop } from '@theokit/agents'`,\n `import { UseGuards } from '@theokit/http'`,\n ``,\n `@Agent({`,\n ` name: '${name}',`,\n ` route: '/api/agents/${name}',`,\n ` model: 'openai/gpt-4o-mini',`,\n ` systemPrompt: 'You are a helpful ${name} assistant.',`,\n `})`,\n `export class ${pascal}Agent {`,\n ` @MainLoop({ strategy: 'react', maxIterations: 5 })`,\n ` async run() {}`,\n `}`,\n ``,\n ].join('\\n')\n}\n\nfunction generateToolboxTemplate(name: string): string {\n const pascal = toPascalCase(name)\n return [\n `import 'reflect-metadata'`,\n `import { z } from 'zod'`,\n `import { Toolbox, Tool } from '@theokit/agents'`,\n ``,\n `@Toolbox({ namespace: '${name}' })`,\n `export class ${pascal}Tools {`,\n ` @Tool({`,\n ` name: 'hello',`,\n ` description: 'Say hello',`,\n ` input: z.object({ name: z.string() }),`,\n ` })`,\n ` async hello(input: { name: string }) {`,\n ` return \\`Hello, \\${input.name}!\\``,\n ` }`,\n `}`,\n ``,\n ].join('\\n')\n}\n\nfunction resolveTemplate(\n cwd: string,\n type: GeneratorType,\n name: string,\n): { filePath: string; content: string } | null {\n switch (type) {\n case 'route':\n return {\n filePath: resolve(cwd, 'server/routes', `${name}.ts`),\n content: generateRouteTemplate(name),\n }\n case 'action':\n return {\n filePath: resolve(cwd, 'server/actions', `${name}.ts`),\n content: generateActionTemplate(name),\n }\n case 'page':\n return { filePath: resolve(cwd, `app/${name}/page.tsx`), content: generatePageTemplate(name) }\n case 'ws':\n return {\n filePath: resolve(cwd, 'server/ws', `${name}.ts`),\n content: generateWsTemplate(name),\n }\n case 'controller':\n return {\n filePath: resolve(cwd, 'server/controllers', `${name}.controller.ts`),\n content: generateControllerTemplate(name),\n }\n case 'agent':\n return {\n filePath: resolve(cwd, 'server/agents', `${name}.agent.ts`),\n content: generateAgentTemplate(name),\n }\n case 'toolbox':\n return {\n filePath: resolve(cwd, 'server/toolboxes', `${name}.tools.ts`),\n content: generateToolboxTemplate(name),\n }\n default:\n return null\n }\n}\n\nfunction generateActionTestTemplate(name: string): string {\n const camel = toCamelCase(name)\n return [\n `import { describe, it, expect } from 'vitest'`,\n ``,\n `import { ${camel} } from './${name.split('/').pop()}.js'`,\n ``,\n `describe('${name} action', () => {`,\n ` it('should accept a valid input shape', () => {`,\n ` expect(${camel}.input).toBeDefined()`,\n ` expect(typeof ${camel}.handler).toBe('function')`,\n ` })`,\n ``,\n ` it('should reject invalid input via zod schema', () => {`,\n ` const parsed = ${camel}.input.safeParse({ __invalid: true })`,\n ` // Empty object schema accepts {}; tighten this assertion when adding fields.`,\n ` expect(parsed.success).toBe(true)`,\n ` })`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generatePageTemplate(name: string): string {\n const pascal = toPascalCase(name)\n return [`export default function ${pascal}Page() {`, ` return <h1>${pascal}</h1>`, `}`, ``].join(\n '\\n',\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\n/**\n * Programmatic generate. Returns a structured result instead of throwing —\n * Studio (`theokit_generate` tool) consumes this directly. The CLI wrapper\n * below maps the structured result to console output + exit code semantics.\n */\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function generate(opts: GenerateOptions): Promise<GenerateResult> {\n const { cwd, type, name } = opts\n\n if (!existsSync(resolve(cwd, 'theo.config.ts')) && !existsSync(resolve(cwd, 'theo.config.js'))) {\n return {\n status: 'not_a_project',\n message: 'Not a Theo project. cwd has no theo.config.ts or theo.config.js',\n }\n }\n\n if (!VALID_TYPES.includes(type as GeneratorType)) {\n return {\n status: 'invalid_kind',\n message: `Invalid generator type \"${type}\". Available: ${VALID_TYPES.join(', ')}`,\n }\n }\n\n if (!name || !toKebabCase(name)) {\n return {\n status: 'invalid_name',\n message: `Invalid name \"${name}\". Use kebab-case: lowercase letters, numbers, hyphens.`,\n }\n }\n\n // EC-2-related: reject reserved JS identifier basenames (would shadow\n // built-in prototype machinery in virtual module emit or scan).\n if (hasReservedSegment(name)) {\n return {\n status: 'invalid_name',\n message: `Reserved name \"${name}\" — basename collides with built-in identifier (index/constructor/__proto__/prototype/hasOwnProperty).`,\n }\n }\n\n // Resource generates multiple files — handle separately\n if (type === 'resource') {\n return generateResource(cwd, name, opts.fields ?? [])\n }\n\n const resolved = resolveTemplate(cwd, type as GeneratorType, name)\n if (resolved === null) {\n return { status: 'invalid_kind', message: `Unknown type: ${type}` }\n }\n const { filePath, content } = resolved\n\n // EC-4: confirm the resolved filePath stays inside cwd. `toKebabCase`\n // already rejects most traversal vectors but a defense-in-depth check\n // against `..` slipping in via valid-looking segments is cheap.\n const relativeFromCwd = filePath.startsWith(resolve(cwd))\n ? filePath.slice(resolve(cwd).length + 1)\n : filePath\n if (!isPathInside(cwd, relativeFromCwd)) {\n return {\n status: 'invalid_name',\n message: `Path traversal denied: \"${name}\" would escape project root.`,\n }\n }\n\n if (existsSync(filePath)) {\n return { status: 'already_exists', filePath, kind: type as GeneratorType, name }\n }\n\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content)\n\n // T5.2 + plan Q4: emit co-located test file alongside actions (only\n // for action type; routes/pages/ws keep prior single-file behavior).\n // Skip if existing test file present (preserve user-customized tests).\n if (type === 'action') {\n const testPath = filePath.replace(/\\.ts$/, '.test.ts')\n if (!existsSync(testPath)) {\n writeFileSync(testPath, generateActionTestTemplate(name))\n }\n }\n\n return { status: 'created', filePath, kind: type as GeneratorType, name }\n}\n\n/**\n * CLI entry point — preserves the original surface (throws + console.log).\n * Wraps the programmatic `generate` function.\n */\nexport async function generateCommand(\n type: string,\n name: string,\n fields?: string[],\n): Promise<void> {\n const result = await generate({ cwd: process.cwd(), type, name, fields })\n switch (result.status) {\n case 'not_a_project':\n throw new Error('Not a Theo project. Run this from a project root with theo.config.ts')\n case 'invalid_kind':\n throw new Error(result.message ?? 'Invalid kind')\n case 'invalid_name':\n throw new Error(\n `Invalid name \"${name}\". Use kebab-case: lowercase letters, numbers, hyphens. Example: my-route`,\n )\n case 'already_exists':\n console.log(`\\n ⚠ ${result.filePath} already exists. Skipping.\\n`)\n return\n case 'created':\n console.log(`\\n ✓ Created ${type}: ${result.filePath}\\n`)\n return\n }\n}\n","import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\n\nimport type { GenerateResult } from './generate-types.js'\n\n// --- Field parsing ---\n\nconst ALLOWED_FIELD_TYPES = new Set(['string', 'text', 'number', 'boolean'])\nconst RESERVED_FIELDS = new Set(['id', 'createdAt', 'created_at'])\n\nexport interface ResourceField {\n name: string\n type: 'string' | 'text' | 'number' | 'boolean'\n drizzleColumn: string\n zodType: string\n}\n\nexport function parseResourceFields(args: string[]): ResourceField[] {\n if (args.length === 0) {\n throw new Error(\n 'Resource requires at least one field. Example: theokit generate resource posts title:string',\n )\n }\n return args.map((arg) => {\n const parts = arg.split(':')\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new Error(`Invalid field \"${arg}\". Use name:type format (e.g. title:string)`)\n }\n const [name, type] = parts\n if (!ALLOWED_FIELD_TYPES.has(type)) {\n throw new Error(\n `Unknown field type \"${type}\" in \"${arg}\". Valid types: ${[...ALLOWED_FIELD_TYPES].join(', ')}`,\n )\n }\n if (RESERVED_FIELDS.has(name)) {\n throw new Error(`Field \"${name}\" is reserved (auto-generated by the framework)`)\n }\n const fieldType = type as ResourceField['type']\n return {\n name,\n type: fieldType,\n drizzleColumn: mapDrizzleColumn(name, fieldType),\n zodType: mapZodType(fieldType),\n }\n })\n}\n\n// --- Column / type mapping ---\n\nfunction mapDrizzleColumn(name: string, type: ResourceField['type']): string {\n switch (type) {\n case 'string':\n case 'text':\n return `text('${name}').notNull()`\n case 'number':\n return `integer('${name}').notNull()`\n case 'boolean':\n return `integer('${name}', { mode: 'boolean' }).notNull().default(false)`\n }\n}\n\nfunction mapZodType(type: ResourceField['type']): string {\n switch (type) {\n case 'string':\n case 'text':\n return 'z.string()'\n case 'number':\n return 'z.number()'\n case 'boolean':\n return 'z.boolean()'\n }\n}\n\n// --- Template generators ---\n\nfunction generateSchemaEntry(resourceName: string, fields: ResourceField[]): string {\n const cols = [\n ` id: integer('id').primaryKey({ autoIncrement: true }),`,\n ...fields.map((f) => ` ${f.name}: ${f.drizzleColumn},`),\n ` createdAt: text('created_at')`,\n ` .notNull()`,\n ` .$defaultFn(() => new Date().toISOString().split('T')[0]),`,\n ]\n return [\n ``,\n `export const ${resourceName} = sqliteTable('${resourceName}', {`,\n ...cols,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateRouteIndex(resourceName: string, fields: ResourceField[]): string {\n const bodyFields = fields.map((f) => ` ${f.name}: ${f.zodType},`).join('\\n')\n return [\n `import { defineRoute } from 'theokit/server/define'`,\n `import { z } from 'zod'`,\n `import { db } from '../../db/index.js'`,\n `import { ${resourceName} } from '../../db/schema.js'`,\n ``,\n `export const GET = defineRoute({`,\n ` handler: () => db.select().from(${resourceName}).all(),`,\n `})`,\n ``,\n `export const POST = defineRoute({`,\n ` body: z.object({`,\n bodyFields,\n ` }),`,\n ` status: 201,`,\n ` handler: ({ body }) => {`,\n ` const result = db.insert(${resourceName}).values(body).returning().get()`,\n ` return result`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateRouteId(resourceName: string, fields: ResourceField[]): string {\n const updateFields = fields.map((f) => ` ${f.name}: ${f.zodType}.optional(),`).join('\\n')\n return [\n `import { defineRoute } from 'theokit/server/define'`,\n `import { z } from 'zod'`,\n `import { db } from '../../db/index.js'`,\n `import { ${resourceName} } from '../../db/schema.js'`,\n `import { eq } from 'drizzle-orm'`,\n ``,\n `export const GET = defineRoute({`,\n ` params: z.object({ id: z.coerce.number() }),`,\n ` handler: ({ params }) => {`,\n ` const item = db.select().from(${resourceName}).where(eq(${resourceName}.id, params.id)).get()`,\n ` if (!item) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,\n ` return item`,\n ` },`,\n `})`,\n ``,\n `export const PUT = defineRoute({`,\n ` params: z.object({ id: z.coerce.number() }),`,\n ` body: z.object({`,\n updateFields,\n ` }),`,\n ` handler: ({ params, body }) => {`,\n ` const result = db.update(${resourceName}).set(body).where(eq(${resourceName}.id, params.id)).returning().get()`,\n ` if (!result) return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })`,\n ` return result`,\n ` },`,\n `})`,\n ``,\n `export const DELETE = defineRoute({`,\n ` params: z.object({ id: z.coerce.number() }),`,\n ` status: 204,`,\n ` handler: ({ params }) => {`,\n ` db.delete(${resourceName}).where(eq(${resourceName}.id, params.id)).run()`,\n ` },`,\n `})`,\n ``,\n ].join('\\n')\n}\n\nfunction generateTestTemplate(resourceName: string): string {\n return [\n `import { describe, it, expect } from 'vitest'`,\n ``,\n `describe('${resourceName} API', () => {`,\n ` it('should have a schema defined', async () => {`,\n ` const schema = await import('../server/db/schema.js')`,\n ` expect(schema.${resourceName}).toBeDefined()`,\n ` })`,\n ``,\n ` it('should have route handlers', async () => {`,\n ` const index = await import('../server/routes/${resourceName}/index.js')`,\n ` expect(index.GET).toBeDefined()`,\n ` expect(index.POST).toBeDefined()`,\n ` })`,\n `})`,\n ``,\n ].join('\\n')\n}\n\n// --- Orchestrator ---\n\nexport function generateResource(cwd: string, name: string, fieldArgs: string[]): GenerateResult {\n let fields: ResourceField[]\n try {\n fields = parseResourceFields(fieldArgs)\n } catch (err) {\n return { status: 'invalid_name', message: err instanceof Error ? err.message : String(err) }\n }\n\n const schemaPath = resolve(cwd, 'server/db/schema.ts')\n const routeIndexPath = resolve(cwd, `server/routes/${name}/index.ts`)\n const routeIdPath = resolve(cwd, `server/routes/${name}/[id].ts`)\n const testPath = resolve(cwd, `tests/${name}.test.ts`)\n\n if (!existsSync(schemaPath)) {\n return {\n status: 'not_a_project',\n message:\n 'server/db/schema.ts not found. Run this from a TheoKit project with database setup.',\n }\n }\n\n if (existsSync(routeIndexPath)) {\n return { status: 'already_exists', filePath: routeIndexPath, kind: 'resource', name }\n }\n\n const existingSchema = readFileSync(schemaPath, 'utf-8')\n if (existingSchema.includes(`export const ${name} =`)) {\n return {\n status: 'already_exists',\n filePath: schemaPath,\n kind: 'resource',\n name,\n message: `Table \"${name}\" already exists in schema.ts`,\n }\n }\n\n // 1. Append schema entry\n appendFileSync(schemaPath, generateSchemaEntry(name, fields))\n\n // 2. Create route files\n mkdirSync(dirname(routeIndexPath), { recursive: true })\n writeFileSync(routeIndexPath, generateRouteIndex(name, fields))\n writeFileSync(routeIdPath, generateRouteId(name, fields))\n\n // 3. Create test file\n mkdirSync(dirname(testPath), { recursive: true })\n if (!existsSync(testPath)) {\n writeFileSync(testPath, generateTestTemplate(name))\n }\n\n return { status: 'created', filePath: routeIndexPath, kind: 'resource', name }\n}\n","/**\n * Shared types for the `theokit generate` CLI command family.\n *\n * Extracted to break the circular dependency between generate.ts ↔ generate-resource.ts\n * (architecture-remediation plan T1.1, 2026-06-12).\n */\n\nexport const VALID_TYPES = [\n 'route',\n 'action',\n 'page',\n 'ws',\n 'controller',\n 'agent',\n 'toolbox',\n 'resource',\n] as const\nexport type GeneratorType = (typeof VALID_TYPES)[number]\n\nexport interface GenerateOptions {\n cwd: string\n type: string\n name: string\n fields?: string[]\n}\n\nexport type GenerateStatus =\n | 'created'\n | 'already_exists'\n | 'invalid_kind'\n | 'invalid_name'\n | 'not_a_project'\n\nexport interface GenerateResult {\n status: GenerateStatus\n filePath?: string\n kind?: GeneratorType\n name?: string\n message?: string\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","resolve","dirname","resolve","existsSync","mkdirSync","dirname","writeFileSync"]}
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import './rate-limit-BdNDZ3vt.js';
6
6
  import 'node:http';
7
7
  import './rate-limit-store-BEJnhWdw.js';
8
8
  import './csrf-readiness-store-CjIoub3U.js';
9
- import './schema-D3v8VB7o.js';
9
+ import './schema-BpH6ivDY.js';
10
10
 
11
11
  /**
12
12
  * G5 T1.3 — formatError hook signature (blueprint ADR D3).
@@ -45,6 +45,23 @@ declare const theoConfigSchema: z.ZodObject<{
45
45
  }, z.core.$strip>>;
46
46
  }, z.core.$strip>>;
47
47
  port: z.ZodDefault<z.ZodNumber>;
48
+ host: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>;
49
+ open: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
50
+ strictPort: z.ZodDefault<z.ZodBoolean>;
51
+ forwardConsole: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
52
+ unhandledErrors: z.ZodOptional<z.ZodBoolean>;
53
+ logLevels: z.ZodOptional<z.ZodArray<z.ZodEnum<{
54
+ error: "error";
55
+ info: "info";
56
+ warn: "warn";
57
+ debug: "debug";
58
+ log: "log";
59
+ }>>>;
60
+ }, z.core.$strip>]>>;
61
+ warmup: z.ZodOptional<z.ZodObject<{
62
+ clientFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
63
+ ssrFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
64
+ }, z.core.$strip>>;
48
65
  ssr: z.ZodDefault<z.ZodBoolean>;
49
66
  ssrStreaming: z.ZodDefault<z.ZodBoolean>;
50
67
  rateLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
@@ -232,14 +249,13 @@ declare const theoConfigSchema: z.ZodObject<{
232
249
  type: z.ZodOptional<z.ZodEnum<{
233
250
  server: "server";
234
251
  worker: "worker";
235
- frontend: "frontend";
236
252
  }>>;
237
253
  port: z.ZodNumber;
238
254
  proxy: z.ZodString;
239
255
  dev: z.ZodString;
240
256
  build: z.ZodOptional<z.ZodString>;
241
257
  start: z.ZodString;
242
- openapi: z.ZodOptional<z.ZodString>;
258
+ openapi: z.ZodOptional<z.ZodURL>;
243
259
  healthcheck: z.ZodDefault<z.ZodString>;
244
260
  cors: z.ZodDefault<z.ZodBoolean>;
245
261
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -304,6 +320,17 @@ declare class TheoProjectError extends Error {
304
320
  * T2.2 of architecture-cleanup so `devtools → core/contracts` is the legal
305
321
  * edge (replacing the prior `devtools → router` violation).
306
322
  */
323
+ /**
324
+ * Dynamic-segment metadata for a file-system route node.
325
+ * Set when the directory name is `[param]` (single segment) or `[...slug]`
326
+ * (catch-all). `generate.ts` turns this into react-router `:param` / `*` syntax.
327
+ */
328
+ interface RouteNodeDynamic {
329
+ /** The param name (`slug` for `[slug]` / `[...slug]`). Always `[A-Za-z0-9_]+`. */
330
+ paramName: string;
331
+ /** True for catch-all `[...slug]`, false for single dynamic `[slug]`. */
332
+ catchAll: boolean;
333
+ }
307
334
  interface RouteNode {
308
335
  segment: string;
309
336
  path: string;
@@ -312,6 +339,8 @@ interface RouteNode {
312
339
  error?: string;
313
340
  loading?: string;
314
341
  notFound?: string;
342
+ /** Present only for dynamic / catch-all segments (additive — static routes omit it). */
343
+ dynamic?: RouteNodeDynamic;
315
344
  children: RouteNode[];
316
345
  }
317
346
 
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  scanRoutes,
9
9
  theoConfigSchema,
10
10
  theoPlugin
11
- } from "./chunk-X3VNROZ7.js";
12
- import "./chunk-DNMSV3R3.js";
11
+ } from "./chunk-SVSUUK4H.js";
12
+ import "./chunk-D7ZH7DTG.js";
13
13
  import "./chunk-7BIM27LV.js";
14
14
  import "./chunk-PIVX3DYW.js";
15
15
  import "./chunk-5QW7IQQU.js";
@@ -23,13 +23,12 @@ import "./chunk-B3L3TJVF.js";
23
23
  import "./chunk-C3ZZ56YZ.js";
24
24
  import "./chunk-7MQOHNHE.js";
25
25
  import "./chunk-X2VVCJ4V.js";
26
- import "./chunk-KJLECZWQ.js";
26
+ import "./chunk-3FYJ7R7N.js";
27
27
  import "./chunk-GY5Q27BJ.js";
28
28
  import "./chunk-Z5IGLBWE.js";
29
29
  import "./chunk-ZEGYW52B.js";
30
30
  import "./chunk-TSLSIRBR.js";
31
31
  import "./chunk-UD3LFGDL.js";
32
- import "./chunk-4HBI7DHP.js";
33
32
  import "./chunk-DGUM43GV.js";
34
33
 
35
34
  // src/config/define-config.ts