@beignet/cli 0.0.22 → 0.0.24
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/CHANGELOG.md +26 -0
- package/README.md +35 -5
- package/dist/choices.d.ts +8 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +4 -0
- package/dist/choices.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +184 -0
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/make.d.ts +4 -3
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +349 -45
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +6 -2
- package/dist/mcp.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +9 -5
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +4 -2
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/db/mysql.js +2 -2
- package/dist/templates/db/postgres.js +2 -2
- package/dist/templates/db/sqlite.js +2 -2
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +1 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +1 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +90 -20
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shell.d.ts.map +1 -1
- package/dist/templates/shell.js +60 -29
- package/dist/templates/shell.js.map +1 -1
- package/dist/templates/todos.js +5 -5
- package/package.json +2 -2
- package/src/choices.ts +10 -0
- package/src/index.ts +11 -0
- package/src/inspect.ts +319 -0
- package/src/lib.ts +2 -1
- package/src/make.ts +449 -53
- package/src/mcp.ts +7 -1
- package/src/templates/agents.ts +9 -5
- package/src/templates/base.ts +4 -2
- package/src/templates/db/mysql.ts +2 -2
- package/src/templates/db/postgres.ts +2 -2
- package/src/templates/db/sqlite.ts +2 -2
- package/src/templates/index.ts +1 -0
- package/src/templates/server.ts +90 -20
- package/src/templates/shell.ts +61 -29
- package/src/templates/todos.ts +5 -5
package/dist/templates/shell.js
CHANGED
|
@@ -1007,12 +1007,40 @@ export function SettingsNav() {
|
|
|
1007
1007
|
);
|
|
1008
1008
|
}
|
|
1009
1009
|
`;
|
|
1010
|
+
const todoClientQueries = `import type { QueryClient } from "@tanstack/react-query";
|
|
1011
|
+
import { rq } from "@/client";
|
|
1012
|
+
import {
|
|
1013
|
+
createTodo,
|
|
1014
|
+
deleteTodo,
|
|
1015
|
+
listTodos,
|
|
1016
|
+
updateTodo,
|
|
1017
|
+
} from "@/features/todos/contracts";
|
|
1018
|
+
|
|
1019
|
+
export function listTodosQueryOptions() {
|
|
1020
|
+
return rq(listTodos).queryOptions();
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
export function createTodoMutationOptions() {
|
|
1024
|
+
return rq(createTodo).mutationOptions();
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
export function updateTodoMutationOptions() {
|
|
1028
|
+
return rq(updateTodo).mutationOptions();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
export function deleteTodoMutationOptions() {
|
|
1032
|
+
return rq(deleteTodo).mutationOptions();
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
export function invalidateTodos(queryClient: QueryClient) {
|
|
1036
|
+
return rq(listTodos).invalidate(queryClient);
|
|
1037
|
+
}
|
|
1038
|
+
`;
|
|
1010
1039
|
const todoApp = `"use client";
|
|
1011
1040
|
|
|
1012
1041
|
import { rootFormError } from "@beignet/react-hook-form";
|
|
1013
1042
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
1014
1043
|
import { Trash2Icon } from "lucide-react";
|
|
1015
|
-
import { rq } from "@/client";
|
|
1016
1044
|
import { rhf } from "@/client/forms";
|
|
1017
1045
|
import { Button } from "@/components/ui/button";
|
|
1018
1046
|
import { Card, CardContent } from "@/components/ui/card";
|
|
@@ -1020,45 +1048,44 @@ import { Checkbox } from "@/components/ui/checkbox";
|
|
|
1020
1048
|
import { Input } from "@/components/ui/input";
|
|
1021
1049
|
import { Label } from "@/components/ui/label";
|
|
1022
1050
|
import {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1051
|
+
createTodoMutationOptions,
|
|
1052
|
+
deleteTodoMutationOptions,
|
|
1053
|
+
invalidateTodos,
|
|
1054
|
+
listTodosQueryOptions,
|
|
1055
|
+
updateTodoMutationOptions,
|
|
1056
|
+
} from "@/features/todos/client/queries";
|
|
1057
|
+
import { createTodo } from "@/features/todos/contracts";
|
|
1028
1058
|
import { cn } from "@/lib/utils";
|
|
1029
1059
|
|
|
1030
1060
|
const createTodoForm = rhf(createTodo);
|
|
1031
1061
|
|
|
1032
1062
|
export function TodoApp() {
|
|
1033
1063
|
const queryClient = useQueryClient();
|
|
1034
|
-
const todosQuery = useQuery(
|
|
1064
|
+
const todosQuery = useQuery(listTodosQueryOptions());
|
|
1035
1065
|
const form = createTodoForm.useForm({
|
|
1036
1066
|
defaultValues: { title: "" },
|
|
1037
1067
|
});
|
|
1038
1068
|
|
|
1039
|
-
const
|
|
1069
|
+
const invalidateTodoQueries = () => invalidateTodos(queryClient);
|
|
1040
1070
|
|
|
1041
|
-
const createTodoMutation = useMutation(
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
onSuccess: invalidateTodos,
|
|
1060
|
-
}),
|
|
1061
|
-
);
|
|
1071
|
+
const createTodoMutation = useMutation({
|
|
1072
|
+
...createTodoMutationOptions(),
|
|
1073
|
+
onSuccess: async () => {
|
|
1074
|
+
form.reset();
|
|
1075
|
+
await invalidateTodoQueries();
|
|
1076
|
+
},
|
|
1077
|
+
onError: (error) => {
|
|
1078
|
+
form.setError("root", rootFormError(error, "Could not create the todo."));
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
const updateTodoMutation = useMutation({
|
|
1082
|
+
...updateTodoMutationOptions(),
|
|
1083
|
+
onSuccess: invalidateTodoQueries,
|
|
1084
|
+
});
|
|
1085
|
+
const deleteTodoMutation = useMutation({
|
|
1086
|
+
...deleteTodoMutationOptions(),
|
|
1087
|
+
onSuccess: invalidateTodoQueries,
|
|
1088
|
+
});
|
|
1062
1089
|
|
|
1063
1090
|
const onSubmit = form.handleSubmit((body) => {
|
|
1064
1091
|
form.clearErrors("root");
|
|
@@ -1186,6 +1213,10 @@ export function shellTemplateFiles(ctx) {
|
|
|
1186
1213
|
{ path: "components/theme-toggle.tsx", content: themeToggle },
|
|
1187
1214
|
{ path: "components/app-sidebar.tsx", content: appSidebar },
|
|
1188
1215
|
{ path: "components/settings-nav.tsx", content: settingsNav },
|
|
1216
|
+
{
|
|
1217
|
+
path: "features/todos/client/queries.ts",
|
|
1218
|
+
content: todoClientQueries,
|
|
1219
|
+
},
|
|
1189
1220
|
{
|
|
1190
1221
|
path: "features/todos/components/todo-app.tsx",
|
|
1191
1222
|
content: todoApp,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/templates/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,aAAa,GAGd,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AAEH,MAAM,YAAY,GAAG;;;;;;;;;;;;;CAapB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmClB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DnB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;CASlB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGlB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHlB,CAAC;AAEF,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BjB,CAAC;AAEF,SAAS,aAAa,CAAC,GAAoB;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA0CA,aAAa,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;CAkB1B,CAAC;AACF,CAAC;AAED,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;CAgBjB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;CAiBtB,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgH3B,CAAC;AAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsG5B,CAAC;AAEF,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;CAyB9B,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoF3B,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;CAmBnB,CAAC;AAEF,MAAM,cAAc,GAAG;;;CAGtB,CAAC;AAEF,MAAM,WAAW,GAAG;;;CAGnB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCnB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGlB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCnB,CAAC;AAEF,MAAM,OAAO,GAAG
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/templates/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,aAAa,GAGd,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AAEH,MAAM,YAAY,GAAG;;;;;;;;;;;;;CAapB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmClB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DnB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;CASlB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGlB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHlB,CAAC;AAEF,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BjB,CAAC;AAEF,SAAS,aAAa,CAAC,GAAoB;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA0CA,aAAa,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;CAkB1B,CAAC;AACF,CAAC;AAED,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;CAgBjB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;CAiBtB,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgH3B,CAAC;AAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsG5B,CAAC;AAEF,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;CAyB9B,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoF3B,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;CAmBnB,CAAC;AAEF,MAAM,cAAc,GAAG;;;CAGtB,CAAC;AAEF,MAAM,WAAW,GAAG;;;CAGnB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCnB,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGlB,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCnB,CAAC;AAEF,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BzB,CAAC;AAEF,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgJf,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,GAAoB;IACrD,OAAO;QACL,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE;QAC/C,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,YAAY,EAAE;QACpD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE;QAC9C,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,UAAU,EAAE;QACtD,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,UAAU,EAAE;QAC5D,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,UAAU,EAAE;QAC5D,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,SAAS,EAAE;QACpD,EAAE,IAAI,EAAE,8BAA8B,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE;QACrE,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,SAAS,EAAE;QACxD,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,cAAc,EAAE;QAClE,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,mBAAmB,EAAE;QACrE;YACE,IAAI,EAAE,sCAAsC;YAC5C,OAAO,EAAE,oBAAoB;SAC9B;QACD;YACE,IAAI,EAAE,wCAAwC;YAC9C,OAAO,EAAE,sBAAsB;SAChC;QACD;YACE,IAAI,EAAE,qCAAqC;YAC3C,OAAO,EAAE,mBAAmB;SAC7B;QACD,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE;QACjD,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,cAAc,EAAE;QAC1D,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE;QACjD,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,aAAa,EAAE;QACjE,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,WAAW,EAAE;QAC7D,EAAE,IAAI,EAAE,4BAA4B,EAAE,OAAO,EAAE,UAAU,EAAE;QAC3D,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,WAAW,EAAE;QAC7D;YACE,IAAI,EAAE,kCAAkC;YACxC,OAAO,EAAE,iBAAiB;SAC3B;QACD;YACE,IAAI,EAAE,wCAAwC;YAC9C,OAAO,EAAE,OAAO;SACjB;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,aAAa,EAAE,gBAAgB,CAAC,UAAU;IAC1C,cAAc,EAAE,SAAS;IACzB,aAAa,EAAE,QAAQ;CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAA2B,EAAE,CAAC"}
|
package/dist/templates/todos.js
CHANGED
|
@@ -174,7 +174,7 @@ export const todoPolicy = definePolicy({
|
|
|
174
174
|
authorizeOwner(ctx, todo, "delete"),
|
|
175
175
|
});
|
|
176
176
|
`,
|
|
177
|
-
routes: `import
|
|
177
|
+
routes: `import "@beignet/core/server-only";
|
|
178
178
|
import { defineRouteGroup } from "@beignet/next";
|
|
179
179
|
import type { AppContext } from "@/app-context";
|
|
180
180
|
import {
|
|
@@ -200,7 +200,7 @@ export const todoRoutes = defineRouteGroup<AppContext>()({
|
|
|
200
200
|
],
|
|
201
201
|
});
|
|
202
202
|
`,
|
|
203
|
-
listTodosUseCase: `import
|
|
203
|
+
listTodosUseCase: `import "@beignet/core/server-only";
|
|
204
204
|
import { normalizeOffsetPage } from "@beignet/core/pagination";
|
|
205
205
|
import { requireUser } from "@/lib/auth";
|
|
206
206
|
import { useCase } from "@/lib/use-case";
|
|
@@ -220,7 +220,7 @@ export const listTodosUseCase = useCase
|
|
|
220
220
|
return ctx.ports.todos.list(user.id, page);
|
|
221
221
|
});
|
|
222
222
|
`,
|
|
223
|
-
createTodoUseCase: `import
|
|
223
|
+
createTodoUseCase: `import "@beignet/core/server-only";
|
|
224
224
|
import { requireUser } from "@/lib/auth";
|
|
225
225
|
import { useCase } from "@/lib/use-case";
|
|
226
226
|
import { CreateTodoInputSchema, TodoSchema } from "../schemas";
|
|
@@ -238,7 +238,7 @@ export const createTodoUseCase = useCase
|
|
|
238
238
|
);
|
|
239
239
|
});
|
|
240
240
|
`,
|
|
241
|
-
updateTodoUseCase: `import
|
|
241
|
+
updateTodoUseCase: `import "@beignet/core/server-only";
|
|
242
242
|
import { appError } from "@/features/shared/errors";
|
|
243
243
|
import { requireUser } from "@/lib/auth";
|
|
244
244
|
import { useCase } from "@/lib/use-case";
|
|
@@ -263,7 +263,7 @@ export const updateTodoUseCase = useCase
|
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
265
|
`,
|
|
266
|
-
deleteTodoUseCase: `import
|
|
266
|
+
deleteTodoUseCase: `import "@beignet/core/server-only";
|
|
267
267
|
import { appError } from "@/features/shared/errors";
|
|
268
268
|
import { requireUser } from "@/lib/auth";
|
|
269
269
|
import { useCase } from "@/lib/use-case";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beignet/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for creating and maintaining Beignet apps.",
|
|
6
6
|
"main": "./dist/lib.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"typescript": "^5.3.0"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@beignet/core": "^0.0.
|
|
71
|
+
"@beignet/core": "^0.0.24",
|
|
72
72
|
"@clack/prompts": "^1.5.1",
|
|
73
73
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
74
74
|
"@stricli/core": "^1.2.7",
|
package/src/choices.ts
CHANGED
|
@@ -156,3 +156,13 @@ export const makeFeatureAddonChoices = [
|
|
|
156
156
|
* Optional artifact accepted by `beignet make feature --with`.
|
|
157
157
|
*/
|
|
158
158
|
export type MakeFeatureAddon = (typeof makeFeatureAddonChoices)[number];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Named recipes supported by `beignet make feature --recipe`.
|
|
162
|
+
*/
|
|
163
|
+
export const makeFeatureRecipeChoices = ["full-slice"] as const;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Recipe accepted by `beignet make feature --recipe`.
|
|
167
|
+
*/
|
|
168
|
+
export type MakeFeatureRecipe = (typeof makeFeatureRecipeChoices)[number];
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
type IntegrationName,
|
|
23
23
|
integrationChoices,
|
|
24
24
|
type MakeFeatureAddon,
|
|
25
|
+
type MakeFeatureRecipe,
|
|
25
26
|
makeFeatureAddonChoices,
|
|
27
|
+
makeFeatureRecipeChoices,
|
|
26
28
|
type PackageManager,
|
|
27
29
|
packageManagerChoices,
|
|
28
30
|
type TemplateName,
|
|
@@ -76,6 +78,7 @@ type MakeResourceFlags = MakeFlags & {
|
|
|
76
78
|
|
|
77
79
|
type MakeFeatureFlags = MakeFlags & {
|
|
78
80
|
with?: readonly MakeFeatureAddon[];
|
|
81
|
+
recipe?: MakeFeatureRecipe;
|
|
79
82
|
};
|
|
80
83
|
|
|
81
84
|
type MakeListenerFlags = MakeFlags & {
|
|
@@ -883,6 +886,13 @@ const makeFeatureCommand = buildCommand<MakeFeatureFlags, [string], CliContext>(
|
|
|
883
886
|
brief:
|
|
884
887
|
"Add feature-owned artifacts. Supports policy, factory/factories, seed/seeds, task/tasks, event/events, listener/listeners, job/jobs, notification/notifications, schedule/schedules, ui, and upload/uploads.",
|
|
885
888
|
},
|
|
889
|
+
recipe: {
|
|
890
|
+
kind: "enum",
|
|
891
|
+
values: makeFeatureRecipeChoices,
|
|
892
|
+
optional: true,
|
|
893
|
+
brief:
|
|
894
|
+
"Generate a named feature recipe. Supports full-slice for policy, client UI, workflow artifacts, outbox-ready events/jobs, and listener registration.",
|
|
895
|
+
},
|
|
886
896
|
} satisfies FlagParametersForType<MakeFeatureFlags, CliContext>,
|
|
887
897
|
positional: namePositional,
|
|
888
898
|
},
|
|
@@ -897,6 +907,7 @@ const makeFeatureCommand = buildCommand<MakeFeatureFlags, [string], CliContext>(
|
|
|
897
907
|
const result = await makeFeature({
|
|
898
908
|
name,
|
|
899
909
|
with: flags.with,
|
|
910
|
+
recipe: flags.recipe,
|
|
900
911
|
force: Boolean(flags.force),
|
|
901
912
|
dryRun: Boolean(flags.dryRun),
|
|
902
913
|
});
|
package/src/inspect.ts
CHANGED
|
@@ -363,10 +363,57 @@ export function formatDoctor(
|
|
|
363
363
|
);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
const checklist = productionHardeningChecklist(result.diagnostics);
|
|
367
|
+
if (checklist.length > 0) {
|
|
368
|
+
lines.push("", paint("Production Hardening Checklist:", "bold"), "");
|
|
369
|
+
lines.push(...checklist.map((item) => `- ${item}`));
|
|
370
|
+
}
|
|
371
|
+
|
|
366
372
|
lines.push("", formatDoctorFooter(result));
|
|
367
373
|
return lines.join("\n");
|
|
368
374
|
}
|
|
369
375
|
|
|
376
|
+
const productionHardeningDiagnosticCodes = new Set([
|
|
377
|
+
"BEIGNET_AUTH_ROUTE_MISSING",
|
|
378
|
+
"BEIGNET_AUTH_TRUSTED_ORIGINS_MISSING",
|
|
379
|
+
"BEIGNET_BETTER_AUTH_SECRET_PLACEHOLDER",
|
|
380
|
+
"BEIGNET_BILLING_CHECKOUT_IDEMPOTENCY_MISSING",
|
|
381
|
+
"BEIGNET_BILLING_ENTITLEMENTS_PROVIDER_MISSING",
|
|
382
|
+
"BEIGNET_BILLING_IDEMPOTENCY_PORT_MISSING",
|
|
383
|
+
"BEIGNET_CORS_CREDENTIALS_WILDCARD",
|
|
384
|
+
"BEIGNET_CRON_SECRET_MISSING",
|
|
385
|
+
"BEIGNET_DEVTOOLS_ROUTE_EXPOSED",
|
|
386
|
+
"BEIGNET_ENTITLEMENTS_PORT_MISSING",
|
|
387
|
+
"BEIGNET_PAYMENTS_STRIPE_MEMORY_PROVIDER",
|
|
388
|
+
"BEIGNET_PAYMENTS_WEBHOOK_ROUTE_MISSING",
|
|
389
|
+
"BEIGNET_PROVIDER_ENV_MISSING",
|
|
390
|
+
"BEIGNET_READINESS_ROUTE_MISSING",
|
|
391
|
+
"BEIGNET_TENANT_HEADER_AUTHORITY",
|
|
392
|
+
"BEIGNET_UPLOAD_AUTHORIZATION_MISSING",
|
|
393
|
+
"BEIGNET_UPLOAD_SIZE_LIMIT_MISSING",
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
function productionHardeningChecklist(
|
|
397
|
+
diagnostics: InspectDiagnostic[],
|
|
398
|
+
): string[] {
|
|
399
|
+
if (
|
|
400
|
+
!diagnostics.some((diagnostic) =>
|
|
401
|
+
productionHardeningDiagnosticCodes.has(diagnostic.code),
|
|
402
|
+
)
|
|
403
|
+
) {
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return [
|
|
408
|
+
"Secrets and provider credentials are unique per environment, validated at startup, never committed, and never logged.",
|
|
409
|
+
"Auth routes, tenant resolution, authorization policies, and trusted origins derive authority from verified sessions, API keys, or trusted gateway metadata.",
|
|
410
|
+
"Devtools, OpenAPI, cron, webhooks, and operational routes have intentional exposure and app-owned authorization.",
|
|
411
|
+
"CORS origins, proxy IP trust, rate-limit keys, and request body limits match the production host topology.",
|
|
412
|
+
"Uploads and storage define max sizes, authorization or explicit public access, object visibility, safe content headers, and short direct-upload expirations.",
|
|
413
|
+
"Provider-backed dependencies have bounded readiness checks, worker shutdown behavior, webhook secrets, and least-privilege credentials.",
|
|
414
|
+
];
|
|
415
|
+
}
|
|
416
|
+
|
|
370
417
|
function formatDoctorFooter(result: InspectAppResult): string {
|
|
371
418
|
const count = (severity: InspectDiagnostic["severity"]) =>
|
|
372
419
|
result.diagnostics.filter((diagnostic) => diagnostic.severity === severity)
|
|
@@ -1974,6 +2021,32 @@ async function inspectProductionReadiness(
|
|
|
1974
2021
|
});
|
|
1975
2022
|
}
|
|
1976
2023
|
|
|
2024
|
+
if (
|
|
2025
|
+
isFeatureUploadDefinition(file, config) &&
|
|
2026
|
+
containsCallExpression(source, "defineUpload") &&
|
|
2027
|
+
!/\bauthorize\s*(?:\(|:)/.test(source) &&
|
|
2028
|
+
!/\baccess\s*:\s*["']public["']/.test(source)
|
|
2029
|
+
) {
|
|
2030
|
+
diagnostics.push({
|
|
2031
|
+
severity: "warning",
|
|
2032
|
+
code: "BEIGNET_UPLOAD_AUTHORIZATION_MISSING",
|
|
2033
|
+
file,
|
|
2034
|
+
message: `${file} defines a protected upload without authorize(...). Add an authorize hook, or set access: "public" only for intentionally public uploads.`,
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
if (
|
|
2039
|
+
isRouteServerOrInfraFile(file, config) &&
|
|
2040
|
+
containsRequestTenantHeaderAuthority(source)
|
|
2041
|
+
) {
|
|
2042
|
+
diagnostics.push({
|
|
2043
|
+
severity: "warning",
|
|
2044
|
+
code: "BEIGNET_TENANT_HEADER_AUTHORITY",
|
|
2045
|
+
file,
|
|
2046
|
+
message: `${file} reads x-tenant-id and turns it into tenant authority. Resolve request tenants from verified auth/session claims or trusted gateway metadata instead of a caller-controlled header.`,
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1977
2050
|
if (
|
|
1978
2051
|
isRouteServerOrInfraFile(file, config) &&
|
|
1979
2052
|
containsCallExpression(source, "createCorsHooks") &&
|
|
@@ -1991,6 +2064,19 @@ async function inspectProductionReadiness(
|
|
|
1991
2064
|
}
|
|
1992
2065
|
}
|
|
1993
2066
|
|
|
2067
|
+
for (const file of configFiles) {
|
|
2068
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
2069
|
+
|
|
2070
|
+
if (containsBetterAuthLocalSecret(source)) {
|
|
2071
|
+
diagnostics.push({
|
|
2072
|
+
severity: "warning",
|
|
2073
|
+
code: "BEIGNET_BETTER_AUTH_SECRET_PLACEHOLDER",
|
|
2074
|
+
file,
|
|
2075
|
+
message: `${file} contains the known local Better Auth placeholder secret. Keep it out of production configuration and require a unique BETTER_AUTH_SECRET before deployment.`,
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
1994
2080
|
const providerEntries = await readProviderListEntries(
|
|
1995
2081
|
targetDir,
|
|
1996
2082
|
files,
|
|
@@ -2138,6 +2224,34 @@ function containsReadinessCheck(source: string): boolean {
|
|
|
2138
2224
|
return /\bchecks\s*:/.test(source) || /\.checkHealth\s*\(/.test(source);
|
|
2139
2225
|
}
|
|
2140
2226
|
|
|
2227
|
+
function containsBetterAuthLocalSecret(source: string): boolean {
|
|
2228
|
+
if (!source.includes("local-dev-better-auth-secret-change-me")) return false;
|
|
2229
|
+
|
|
2230
|
+
const hasProductionRuntimeGuard =
|
|
2231
|
+
/\bisProductionRuntime\s*=\s*process\.env\.NODE_ENV\s*===\s*["']production["']\s*&&\s*process\.env\.NEXT_PHASE\s*!==\s*["']phase-production-build["']/.test(
|
|
2232
|
+
source,
|
|
2233
|
+
) &&
|
|
2234
|
+
/BETTER_AUTH_SECRET\s*:\s*isProductionRuntime\s*\?\s*BetterAuthSecret\s*:\s*BetterAuthSecret\.default\(LOCAL_BETTER_AUTH_SECRET\)/s.test(
|
|
2235
|
+
source,
|
|
2236
|
+
);
|
|
2237
|
+
const hasProductionGuard =
|
|
2238
|
+
/BETTER_AUTH_SECRET\s*:\s*process\.env\.NODE_ENV\s*===\s*["']production["']\s*\?\s*BetterAuthSecret\s*:\s*BetterAuthSecret\.default\(LOCAL_BETTER_AUTH_SECRET\)/s.test(
|
|
2239
|
+
source,
|
|
2240
|
+
);
|
|
2241
|
+
|
|
2242
|
+
return !(hasProductionRuntimeGuard || hasProductionGuard);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
function containsRequestTenantHeaderAuthority(source: string): boolean {
|
|
2246
|
+
return (
|
|
2247
|
+
/(?:(?:\breq|\brequest)\.headers|(?<!\.)\bheaders)\.get\(\s*["']x-tenant-id["']\s*\)/.test(
|
|
2248
|
+
source,
|
|
2249
|
+
) &&
|
|
2250
|
+
(/\bcreateTenant\s*\(/.test(source) ||
|
|
2251
|
+
/\btenant\s*:\s*\{?\s*id\s*:/.test(source))
|
|
2252
|
+
);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2141
2255
|
async function inspectEntitlementsProductionReadiness(
|
|
2142
2256
|
targetDir: string,
|
|
2143
2257
|
files: string[],
|
|
@@ -4091,6 +4205,7 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4091
4205
|
if (!convention.resourceGenerator) return [];
|
|
4092
4206
|
|
|
4093
4207
|
const diagnostics: InspectDiagnostic[] = [];
|
|
4208
|
+
const sourceCache = new Map<string, string>();
|
|
4094
4209
|
const resources = await collectResourceNames(targetDir, files, config);
|
|
4095
4210
|
const infrastructurePath = directoryPath(
|
|
4096
4211
|
path.dirname(config.paths.infrastructurePorts),
|
|
@@ -4149,6 +4264,17 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4149
4264
|
message: `This app has Drizzle database artifacts, but package.json does not define "${script}". Add an app-owned ${script} script so beignet db ${script.replace("db:", "")} can run the database lifecycle command.`,
|
|
4150
4265
|
});
|
|
4151
4266
|
}
|
|
4267
|
+
|
|
4268
|
+
diagnostics.push(
|
|
4269
|
+
...(await inspectDurableDrizzleTableDrift(
|
|
4270
|
+
targetDir,
|
|
4271
|
+
files,
|
|
4272
|
+
config,
|
|
4273
|
+
infrastructurePath,
|
|
4274
|
+
schemaDir,
|
|
4275
|
+
sourceCache,
|
|
4276
|
+
)),
|
|
4277
|
+
);
|
|
4152
4278
|
}
|
|
4153
4279
|
|
|
4154
4280
|
const schemaFilePath = `${infrastructurePath}/db/schema.ts`;
|
|
@@ -4305,6 +4431,199 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4305
4431
|
return diagnostics;
|
|
4306
4432
|
}
|
|
4307
4433
|
|
|
4434
|
+
type DurableDrizzleTableRequirement = {
|
|
4435
|
+
capability: string;
|
|
4436
|
+
defaultTableName: string;
|
|
4437
|
+
code: string;
|
|
4438
|
+
factoryPattern: string;
|
|
4439
|
+
setupPattern?: string;
|
|
4440
|
+
};
|
|
4441
|
+
|
|
4442
|
+
const durableDrizzleTableRequirements: readonly DurableDrizzleTableRequirement[] =
|
|
4443
|
+
[
|
|
4444
|
+
{
|
|
4445
|
+
capability: "idempotency",
|
|
4446
|
+
defaultTableName: "idempotency_records",
|
|
4447
|
+
code: "BEIGNET_IDEMPOTENCY_TABLE_MISSING",
|
|
4448
|
+
factoryPattern: "createDrizzle[A-Za-z]+IdempotencyPort",
|
|
4449
|
+
setupPattern: "createDrizzle[A-Za-z]+IdempotencySetupStatements",
|
|
4450
|
+
},
|
|
4451
|
+
{
|
|
4452
|
+
capability: "outbox",
|
|
4453
|
+
defaultTableName: "outbox_messages",
|
|
4454
|
+
code: "BEIGNET_OUTBOX_TABLE_MISSING",
|
|
4455
|
+
factoryPattern: "createDrizzle[A-Za-z]+OutboxPort",
|
|
4456
|
+
setupPattern: "createDrizzle[A-Za-z]+OutboxSetupStatements",
|
|
4457
|
+
},
|
|
4458
|
+
{
|
|
4459
|
+
capability: "audit",
|
|
4460
|
+
defaultTableName: "audit_log",
|
|
4461
|
+
code: "BEIGNET_AUDIT_TABLE_MISSING",
|
|
4462
|
+
factoryPattern: "createDrizzle(?:[A-Za-z]+AuditLogPort|AuditLog)",
|
|
4463
|
+
setupPattern: "createDrizzle[A-Za-z]+AuditLogSetupStatements",
|
|
4464
|
+
},
|
|
4465
|
+
];
|
|
4466
|
+
|
|
4467
|
+
async function inspectDurableDrizzleTableDrift(
|
|
4468
|
+
targetDir: string,
|
|
4469
|
+
files: string[],
|
|
4470
|
+
config: ResolvedBeignetConfig,
|
|
4471
|
+
infrastructurePath: string,
|
|
4472
|
+
schemaDir: string,
|
|
4473
|
+
sourceCache: Map<string, string>,
|
|
4474
|
+
): Promise<InspectDiagnostic[]> {
|
|
4475
|
+
const diagnostics: InspectDiagnostic[] = [];
|
|
4476
|
+
const usageFiles = durableDrizzleUsageFiles(files, config);
|
|
4477
|
+
|
|
4478
|
+
for (const requirement of durableDrizzleTableRequirements) {
|
|
4479
|
+
const tableNames = await configuredDurableDrizzleTableNames(
|
|
4480
|
+
targetDir,
|
|
4481
|
+
usageFiles,
|
|
4482
|
+
requirement,
|
|
4483
|
+
sourceCache,
|
|
4484
|
+
);
|
|
4485
|
+
if (tableNames.size === 0) continue;
|
|
4486
|
+
|
|
4487
|
+
for (const tableName of tableNames) {
|
|
4488
|
+
if (
|
|
4489
|
+
await durableDrizzleTableExists(
|
|
4490
|
+
targetDir,
|
|
4491
|
+
files,
|
|
4492
|
+
infrastructurePath,
|
|
4493
|
+
schemaDir,
|
|
4494
|
+
requirement,
|
|
4495
|
+
tableName,
|
|
4496
|
+
sourceCache,
|
|
4497
|
+
)
|
|
4498
|
+
) {
|
|
4499
|
+
continue;
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4502
|
+
diagnostics.push({
|
|
4503
|
+
severity: "warning",
|
|
4504
|
+
code: requirement.code,
|
|
4505
|
+
file: schemaDir,
|
|
4506
|
+
message: `This app wires a Drizzle-backed ${requirement.capability} port, but no ${tableName} table setup was found in ${schemaDir}/, drizzle/, or app database setup files. Add the table to your Drizzle schema/migrations, call the provider setup statements, or remove the stale ${requirement.capability} wiring before deployment.`,
|
|
4507
|
+
});
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
return diagnostics;
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
function durableDrizzleUsageFiles(
|
|
4515
|
+
files: string[],
|
|
4516
|
+
config: ResolvedBeignetConfig,
|
|
4517
|
+
): string[] {
|
|
4518
|
+
return files.filter(
|
|
4519
|
+
(file) =>
|
|
4520
|
+
/\.(?:ts|tsx|mts|cts)$/.test(file) &&
|
|
4521
|
+
!isTestSourceFile(file) &&
|
|
4522
|
+
isInfraOrServerFile(file, config),
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
async function configuredDurableDrizzleTableNames(
|
|
4527
|
+
targetDir: string,
|
|
4528
|
+
files: string[],
|
|
4529
|
+
requirement: DurableDrizzleTableRequirement,
|
|
4530
|
+
sourceCache: Map<string, string>,
|
|
4531
|
+
): Promise<Set<string>> {
|
|
4532
|
+
const tableNames = new Set<string>();
|
|
4533
|
+
|
|
4534
|
+
for (const file of files) {
|
|
4535
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4536
|
+
for (const tableName of tableNamesFromCalls(
|
|
4537
|
+
source,
|
|
4538
|
+
requirement.factoryPattern,
|
|
4539
|
+
requirement.defaultTableName,
|
|
4540
|
+
)) {
|
|
4541
|
+
tableNames.add(tableName);
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
return tableNames;
|
|
4546
|
+
}
|
|
4547
|
+
|
|
4548
|
+
async function durableDrizzleTableExists(
|
|
4549
|
+
targetDir: string,
|
|
4550
|
+
files: string[],
|
|
4551
|
+
infrastructurePath: string,
|
|
4552
|
+
schemaDir: string,
|
|
4553
|
+
requirement: DurableDrizzleTableRequirement,
|
|
4554
|
+
tableName: string,
|
|
4555
|
+
sourceCache: Map<string, string>,
|
|
4556
|
+
): Promise<boolean> {
|
|
4557
|
+
const schemaFiles = files.filter(
|
|
4558
|
+
(file) =>
|
|
4559
|
+
file.startsWith(`${schemaDir}/`) &&
|
|
4560
|
+
/\.(?:ts|mts|cts)$/.test(file) &&
|
|
4561
|
+
!isTestSourceFile(file),
|
|
4562
|
+
);
|
|
4563
|
+
for (const file of schemaFiles) {
|
|
4564
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4565
|
+
if (source.includes(tableName)) return true;
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4568
|
+
const setupFiles = files.filter(
|
|
4569
|
+
(file) =>
|
|
4570
|
+
!isTestSourceFile(file) &&
|
|
4571
|
+
(file.startsWith("drizzle/") ||
|
|
4572
|
+
file.startsWith(`${infrastructurePath}/db/`)) &&
|
|
4573
|
+
/\.(?:ts|js|mts|mjs|cts|cjs|sql)$/.test(file),
|
|
4574
|
+
);
|
|
4575
|
+
for (const file of setupFiles) {
|
|
4576
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4577
|
+
if (sourceCreatesTable(source, tableName)) return true;
|
|
4578
|
+
|
|
4579
|
+
if (!requirement.setupPattern) continue;
|
|
4580
|
+
if (
|
|
4581
|
+
tableNamesFromCalls(
|
|
4582
|
+
source,
|
|
4583
|
+
requirement.setupPattern,
|
|
4584
|
+
requirement.defaultTableName,
|
|
4585
|
+
).has(tableName)
|
|
4586
|
+
) {
|
|
4587
|
+
return true;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
|
|
4591
|
+
return false;
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
function sourceCreatesTable(source: string, tableName: string): boolean {
|
|
4595
|
+
return new RegExp(
|
|
4596
|
+
`\\bCREATE\\s+TABLE\\b[\\s\\S]{0,200}${escapeRegExp(tableName)}\\b`,
|
|
4597
|
+
"i",
|
|
4598
|
+
).test(source);
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
function tableNamesFromCalls(
|
|
4602
|
+
source: string,
|
|
4603
|
+
calleePattern: string,
|
|
4604
|
+
defaultTableName: string,
|
|
4605
|
+
): Set<string> {
|
|
4606
|
+
const tableNames = new Set<string>();
|
|
4607
|
+
const callPattern = new RegExp(
|
|
4608
|
+
`\\b${calleePattern}\\s*\\(([\\s\\S]{0,800}?)\\)`,
|
|
4609
|
+
"g",
|
|
4610
|
+
);
|
|
4611
|
+
|
|
4612
|
+
for (const match of source.matchAll(callPattern)) {
|
|
4613
|
+
const matchIndex = match.index ?? 0;
|
|
4614
|
+
const prefix = source.slice(Math.max(0, matchIndex - 32), matchIndex);
|
|
4615
|
+
if (/\bfunction\s+$/.test(prefix)) continue;
|
|
4616
|
+
|
|
4617
|
+
const argsSource = match[1] ?? "";
|
|
4618
|
+
const tableName = /\btableName\s*:\s*["'`]([^"'`]+)["'`]/.exec(
|
|
4619
|
+
argsSource,
|
|
4620
|
+
)?.[1];
|
|
4621
|
+
tableNames.add(tableName ?? defaultTableName);
|
|
4622
|
+
}
|
|
4623
|
+
|
|
4624
|
+
return tableNames;
|
|
4625
|
+
}
|
|
4626
|
+
|
|
4308
4627
|
function hasRepositoryAdapterFile(
|
|
4309
4628
|
files: string[],
|
|
4310
4629
|
infrastructureDir: string,
|
package/src/lib.ts
CHANGED
|
@@ -18,7 +18,7 @@ export {
|
|
|
18
18
|
inspectApp,
|
|
19
19
|
} from "./inspect.js";
|
|
20
20
|
export { formatLint, formatLintGithub, lintApp } from "./lint.js";
|
|
21
|
-
export type { MakeFeatureAddon } from "./make.js";
|
|
21
|
+
export type { MakeFeatureAddon, MakeFeatureRecipe } from "./make.js";
|
|
22
22
|
export {
|
|
23
23
|
makeAdapter,
|
|
24
24
|
makeContract,
|
|
@@ -26,6 +26,7 @@ export {
|
|
|
26
26
|
makeFactory,
|
|
27
27
|
makeFeature,
|
|
28
28
|
makeFeatureAddonChoices,
|
|
29
|
+
makeFeatureRecipeChoices,
|
|
29
30
|
makeJob,
|
|
30
31
|
makeListener,
|
|
31
32
|
makeNotification,
|