@beignet/cli 0.0.34 → 0.0.36
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 +29 -0
- package/README.md +27 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/inspect.js +49 -13
- package/dist/inspect.js.map +1 -1
- package/dist/lint.js +10 -3
- package/dist/lint.js.map +1 -1
- package/dist/make/inbox.d.ts.map +1 -1
- package/dist/make/inbox.js +2 -3
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +3 -5
- package/dist/make/payments.js.map +1 -1
- package/dist/make/tenancy.d.ts.map +1 -1
- package/dist/make/tenancy.js +2 -4
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.js +4 -5
- package/dist/make.js.map +1 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +1 -1
- package/dist/schedule.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +2 -0
- package/dist/templates/agents.js.map +1 -1
- 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 +8 -2
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/testing.d.ts.map +1 -1
- package/dist/templates/testing.js +55 -26
- package/dist/templates/testing.js.map +1 -1
- package/dist/templates/todos.d.ts.map +1 -1
- package/dist/templates/todos.js +2 -3
- package/dist/templates/todos.js.map +1 -1
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +12 -1
- package/src/config.ts +2 -0
- package/src/inspect.ts +82 -15
- package/src/lint.ts +15 -3
- package/src/make/inbox.ts +2 -3
- package/src/make/payments.ts +2 -6
- package/src/make/tenancy.ts +2 -8
- package/src/make.ts +4 -5
- package/src/schedule.ts +4 -3
- package/src/templates/agents.ts +2 -0
- package/src/templates/index.ts +1 -0
- package/src/templates/server.ts +8 -2
- package/src/templates/testing.ts +55 -26
- package/src/templates/todos.ts +2 -3
- package/src/test-helpers/generated-app.ts +1 -1
package/src/make/tenancy.ts
CHANGED
|
@@ -2041,13 +2041,7 @@ export const WorkspaceInviteNotification = defineNotification(
|
|
|
2041
2041
|
}
|
|
2042
2042
|
|
|
2043
2043
|
export function tenancyRoutesFile(config: ResolvedBeignetConfig): string {
|
|
2044
|
-
|
|
2045
|
-
config.paths.features,
|
|
2046
|
-
"workspaces/routes.ts",
|
|
2047
|
-
);
|
|
2048
|
-
|
|
2049
|
-
return `import { defineRouteGroup } from "@beignet/core/server";
|
|
2050
|
-
import type { AppContext } from "${relativeModule(routeFilePath, config.paths.appContext)}";
|
|
2044
|
+
return `import { defineRouteGroup } from "${aliasModule(config.paths.routesBuilder)}";
|
|
2051
2045
|
import * as workspaceContracts from "@/features/workspaces/contracts";
|
|
2052
2046
|
import * as workspaceUseCases from "@/features/workspaces/use-cases";
|
|
2053
2047
|
import { WORKSPACE_COOKIE_NAME } from "@/lib/tenant";
|
|
@@ -2064,7 +2058,7 @@ function workspaceCookie(workspaceId: string): string {
|
|
|
2064
2058
|
\t].join("; ");
|
|
2065
2059
|
}
|
|
2066
2060
|
|
|
2067
|
-
export const workspaceRoutes = defineRouteGroup
|
|
2061
|
+
export const workspaceRoutes = defineRouteGroup({
|
|
2068
2062
|
\tname: "workspaces",
|
|
2069
2063
|
\troutes: [
|
|
2070
2064
|
\t\t{
|
package/src/make.ts
CHANGED
|
@@ -4871,8 +4871,8 @@ function listenerFile(
|
|
|
4871
4871
|
return `import { defineListener } from "${aliasModule(config.paths.listenersBuilder)}";
|
|
4872
4872
|
import { ${names.event.eventExportName} } from "${relativeModule(filePath, eventFilePath(names.event, config))}";
|
|
4873
4873
|
|
|
4874
|
-
export const ${names.listenerExportName} = defineListener(${names.
|
|
4875
|
-
\
|
|
4874
|
+
export const ${names.listenerExportName} = defineListener("${names.listenerName}", {
|
|
4875
|
+
\tevent: ${names.event.eventExportName},
|
|
4876
4876
|
\tasync handle({ payload, ctx }) {
|
|
4877
4877
|
\t\tctx.ports.logger.info("Listener handled", {
|
|
4878
4878
|
\t\t\tlistenerName: "${names.listenerName}",
|
|
@@ -5249,8 +5249,7 @@ function routeGroupFile(
|
|
|
5249
5249
|
const useCasesPath = resourceUseCaseIndexPath(names, config);
|
|
5250
5250
|
|
|
5251
5251
|
return `import "@beignet/core/server-only";
|
|
5252
|
-
import { defineRouteGroup } from "
|
|
5253
|
-
import type { AppContext } from "${relativeModule(routeFilePath, config.paths.appContext)}";
|
|
5252
|
+
import { defineRouteGroup } from "${relativeModule(routeFilePath, config.paths.routesBuilder)}";
|
|
5254
5253
|
import {
|
|
5255
5254
|
create${names.singularPascal},
|
|
5256
5255
|
${mode === "resource" ? ` delete${names.singularPascal},\n get${names.singularPascal},\n` : ""} list${names.pluralPascal},
|
|
@@ -5260,7 +5259,7 @@ import {
|
|
|
5260
5259
|
${mode === "resource" ? ` delete${names.singularPascal}UseCase,\n get${names.singularPascal}UseCase,\n` : ""} list${names.pluralPascal}UseCase,
|
|
5261
5260
|
${mode === "resource" ? ` update${names.singularPascal}UseCase,\n` : ""}} from "${relativeModule(routeFilePath, useCasesPath)}";
|
|
5262
5261
|
|
|
5263
|
-
export const ${names.singularCamel}Routes = defineRouteGroup
|
|
5262
|
+
export const ${names.singularCamel}Routes = defineRouteGroup({
|
|
5264
5263
|
name: "${names.pluralKebab}",
|
|
5265
5264
|
routes: [
|
|
5266
5265
|
{ contract: list${names.pluralPascal}, useCase: list${names.pluralPascal}UseCase },
|
package/src/schedule.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import type { ProviderInstrumentationPort } from "@beignet/core/providers";
|
|
3
4
|
import type {
|
|
4
5
|
ScheduleDef,
|
|
5
|
-
ScheduleInstrumentation,
|
|
6
6
|
ScheduleRunOptions,
|
|
7
7
|
StandardSchema,
|
|
8
8
|
} from "@beignet/core/schedules";
|
|
@@ -54,7 +54,8 @@ type ScheduleContext = {
|
|
|
54
54
|
traceId?: string;
|
|
55
55
|
ports?: {
|
|
56
56
|
logger?: ScheduleLogger;
|
|
57
|
-
devtools?:
|
|
57
|
+
devtools?: ProviderInstrumentationPort;
|
|
58
|
+
instrumentation?: ProviderInstrumentationPort;
|
|
58
59
|
};
|
|
59
60
|
};
|
|
60
61
|
|
|
@@ -107,7 +108,7 @@ export async function runAppSchedule(
|
|
|
107
108
|
);
|
|
108
109
|
const runner = createInlineScheduleRunner<unknown>({
|
|
109
110
|
ctx,
|
|
110
|
-
instrumentation: scheduleContext.ports
|
|
111
|
+
instrumentation: scheduleContext.ports,
|
|
111
112
|
instrumentationContext: {
|
|
112
113
|
requestId: scheduleContext.requestId,
|
|
113
114
|
traceId: scheduleContext.traceId,
|
package/src/templates/agents.ts
CHANGED
|
@@ -48,6 +48,8 @@ is a silent failure — the file exists but never runs:
|
|
|
48
48
|
|
|
49
49
|
- Feature route groups in \`features/<feature>/routes.ts\` must be composed in
|
|
50
50
|
\`server/routes.ts\` via \`defineRoutes([...])\`.
|
|
51
|
+
- Feature route files import the app-bound \`defineRouteGroup\` from
|
|
52
|
+
\`lib/routes.ts\`; that builder owns the shared \`AppContext\` binding.
|
|
51
53
|
- Schedules must be added to the \`schedules\` array in \`server/schedules.ts\`.
|
|
52
54
|
- Tasks must be added to \`defineTasks([...])\` in \`server/tasks.ts\`.
|
|
53
55
|
- Outbox events and jobs must be registered in \`defineOutboxRegistry({...})\`
|
package/src/templates/index.ts
CHANGED
|
@@ -136,6 +136,7 @@ export function getTemplateFiles(ctx: TemplateContext): TemplateFile[] {
|
|
|
136
136
|
{ path: "lib/tenant.ts", content: serverFiles.tenantHelper },
|
|
137
137
|
...testSupportTemplateFiles,
|
|
138
138
|
{ path: "lib/use-case.ts", content: serverFiles.useCaseBuilder },
|
|
139
|
+
{ path: "lib/routes.ts", content: serverFiles.routesBuilder },
|
|
139
140
|
{ path: "server/index.ts", content: server(ctx) },
|
|
140
141
|
{ path: "server/context.ts", content: serverFiles.serverContext },
|
|
141
142
|
{ path: "server/routes.ts", content: serverFiles.serverRoutes },
|
package/src/templates/server.ts
CHANGED
|
@@ -10,7 +10,7 @@ export function serverProviders(ctx: TemplateContext): string {
|
|
|
10
10
|
const imports = [
|
|
11
11
|
'import "@beignet/core/server-only";',
|
|
12
12
|
'import { createDevtoolsProvider } from "@beignet/devtools";',
|
|
13
|
-
'import {
|
|
13
|
+
'import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";',
|
|
14
14
|
`import { ${db.providerFactory} } from "@beignet/provider-db-drizzle/${db.subpath}";`,
|
|
15
15
|
hasIntegration(ctx, "inngest")
|
|
16
16
|
? 'import { createInngestJobsProvider } from "@beignet/provider-jobs-inngest";'
|
|
@@ -30,7 +30,7 @@ export function serverProviders(ctx: TemplateContext): string {
|
|
|
30
30
|
|
|
31
31
|
const entries = [
|
|
32
32
|
"\tcreateDevtoolsProvider(),",
|
|
33
|
-
"\
|
|
33
|
+
"\tcreateBetterAuthProvider<AuthUser, AuthSessionMetadata>(auth),",
|
|
34
34
|
"\tcreatePinoLoggerProvider(),",
|
|
35
35
|
`\t${db.providerInstance},`,
|
|
36
36
|
"\tstarterDatabaseProvider,",
|
|
@@ -396,6 +396,12 @@ import { createUseCase } from "@beignet/core/application";
|
|
|
396
396
|
import type { AppContext } from "@/app-context";
|
|
397
397
|
|
|
398
398
|
export const useCase = createUseCase<AppContext>();
|
|
399
|
+
`,
|
|
400
|
+
routesBuilder: `import "@beignet/core/server-only";
|
|
401
|
+
import { createRoutes } from "@beignet/core/server";
|
|
402
|
+
import type { AppContext } from "@/app-context";
|
|
403
|
+
|
|
404
|
+
export const { defineRoute, defineRouteGroup } = createRoutes<AppContext>();
|
|
399
405
|
`,
|
|
400
406
|
authHelpers: `import type { AppContext } from "@/app-context";
|
|
401
407
|
import type { AuthSession, AuthUser } from "@/ports/auth";
|
package/src/templates/testing.ts
CHANGED
|
@@ -406,6 +406,7 @@ export const testRunnerTemplateFile: TemplateFile = {
|
|
|
406
406
|
content: `import { spawnSync } from "node:child_process";
|
|
407
407
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
408
408
|
import path from "node:path";
|
|
409
|
+
import { pathToFileURL } from "node:url";
|
|
409
410
|
|
|
410
411
|
const defaultTestRoots = [
|
|
411
412
|
"app",
|
|
@@ -428,11 +429,18 @@ const ignoredDirectories = new Set([
|
|
|
428
429
|
]);
|
|
429
430
|
const testFilePattern = /\\.(test|spec)\\.[cm]?[tj]sx?$/;
|
|
430
431
|
|
|
432
|
+
const configFileNames = [
|
|
433
|
+
"beignet.config.ts",
|
|
434
|
+
"beignet.config.json",
|
|
435
|
+
"beignet.config.mjs",
|
|
436
|
+
"beignet.config.js",
|
|
437
|
+
];
|
|
438
|
+
|
|
431
439
|
const explicitArgs = process.argv.slice(2);
|
|
432
440
|
const testArgs =
|
|
433
441
|
explicitArgs.length > 0
|
|
434
442
|
? resolveTestArgs(process.cwd(), explicitArgs)
|
|
435
|
-
: discoverTestFiles(process.cwd());
|
|
443
|
+
: await discoverTestFiles(process.cwd());
|
|
436
444
|
|
|
437
445
|
if (testArgs.length === 0) {
|
|
438
446
|
console.log("No test files found.");
|
|
@@ -456,9 +464,9 @@ if (result.error) {
|
|
|
456
464
|
|
|
457
465
|
process.exit(result.status ?? 1);
|
|
458
466
|
|
|
459
|
-
function discoverTestFiles(cwd: string): string[] {
|
|
467
|
+
async function discoverTestFiles(cwd: string): Promise<string[]> {
|
|
460
468
|
const files: string[] = [];
|
|
461
|
-
for (const root of testRoots(cwd)) {
|
|
469
|
+
for (const root of await testRoots(cwd)) {
|
|
462
470
|
const rootPath = path.join(cwd, root);
|
|
463
471
|
if (existsSync(rootPath)) {
|
|
464
472
|
walk(rootPath, cwd, files);
|
|
@@ -494,32 +502,53 @@ function resolveTestArgs(cwd: string, args: string[]): string[] {
|
|
|
494
502
|
return Array.from(new Set(resolved));
|
|
495
503
|
}
|
|
496
504
|
|
|
497
|
-
function testRoots(cwd: string): string[] {
|
|
498
|
-
return Array.from(
|
|
505
|
+
async function testRoots(cwd: string): Promise<string[]> {
|
|
506
|
+
return Array.from(
|
|
507
|
+
new Set([...defaultTestRoots, ...(await configuredRoots(cwd))]),
|
|
508
|
+
);
|
|
499
509
|
}
|
|
500
510
|
|
|
501
|
-
function configuredRoots(cwd: string): string[] {
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
paths.routes ? path.dirname(paths.routes) : undefined,
|
|
513
|
-
paths.server ? path.dirname(paths.server) : undefined,
|
|
514
|
-
paths.infrastructurePorts
|
|
515
|
-
? path.dirname(paths.infrastructurePorts)
|
|
516
|
-
: undefined,
|
|
517
|
-
paths.ports ? path.dirname(paths.ports) : undefined,
|
|
518
|
-
paths.useCaseBuilder ? path.dirname(paths.useCaseBuilder) : undefined,
|
|
519
|
-
].filter((root): root is string => Boolean(root) && root !== ".");
|
|
520
|
-
} catch {
|
|
521
|
-
return [];
|
|
511
|
+
async function configuredRoots(cwd: string): Promise<string[]> {
|
|
512
|
+
const configFiles = configFileNames.filter((file) =>
|
|
513
|
+
existsSync(path.join(cwd, file)),
|
|
514
|
+
);
|
|
515
|
+
if (configFiles.length === 0) return [];
|
|
516
|
+
if (configFiles.length > 1) {
|
|
517
|
+
throw new Error(
|
|
518
|
+
"Multiple Beignet config files found: " +
|
|
519
|
+
configFiles.join(", ") +
|
|
520
|
+
". Keep only one config file.",
|
|
521
|
+
);
|
|
522
522
|
}
|
|
523
|
+
|
|
524
|
+
const configFile = configFiles[0];
|
|
525
|
+
const configPath = path.join(cwd, configFile);
|
|
526
|
+
const config = configFile.endsWith(".json")
|
|
527
|
+
? JSON.parse(readFileSync(configPath, "utf8"))
|
|
528
|
+
: ((await import(pathToFileURL(configPath).href)) as { default?: unknown })
|
|
529
|
+
.default;
|
|
530
|
+
if (!isObjectRecord(config)) {
|
|
531
|
+
throw new Error(configFile + " must export a Beignet config object.");
|
|
532
|
+
}
|
|
533
|
+
const paths = isObjectRecord(config.paths) ? config.paths : {};
|
|
534
|
+
return [
|
|
535
|
+
paths.tests,
|
|
536
|
+
paths.features,
|
|
537
|
+
parentPath(paths.routes),
|
|
538
|
+
parentPath(paths.server),
|
|
539
|
+
parentPath(paths.infrastructurePorts),
|
|
540
|
+
parentPath(paths.ports),
|
|
541
|
+
parentPath(paths.useCaseBuilder),
|
|
542
|
+
parentPath(paths.routesBuilder),
|
|
543
|
+
].filter((root): root is string => Boolean(root) && root !== ".");
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function parentPath(value: unknown): string | undefined {
|
|
547
|
+
return typeof value === "string" ? path.dirname(value) : undefined;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function isObjectRecord(value: unknown): value is Record<string, unknown> {
|
|
551
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
523
552
|
}
|
|
524
553
|
|
|
525
554
|
function walk(directory: string, cwd: string, files: string[]): void {
|
package/src/templates/todos.ts
CHANGED
|
@@ -175,8 +175,7 @@ export const todoPolicy = definePolicy({
|
|
|
175
175
|
});
|
|
176
176
|
`,
|
|
177
177
|
routes: `import "@beignet/core/server-only";
|
|
178
|
-
import { defineRouteGroup } from "
|
|
179
|
-
import type { AppContext } from "@/app-context";
|
|
178
|
+
import { defineRouteGroup } from "@/lib/routes";
|
|
180
179
|
import {
|
|
181
180
|
createTodo,
|
|
182
181
|
deleteTodo,
|
|
@@ -190,7 +189,7 @@ import {
|
|
|
190
189
|
updateTodoUseCase,
|
|
191
190
|
} from "@/features/todos/use-cases";
|
|
192
191
|
|
|
193
|
-
export const todoRoutes = defineRouteGroup
|
|
192
|
+
export const todoRoutes = defineRouteGroup({
|
|
194
193
|
name: "todos",
|
|
195
194
|
routes: [
|
|
196
195
|
{ contract: listTodos, useCase: listTodosUseCase },
|
|
@@ -116,7 +116,7 @@ export async function linkSmokeDependencies(
|
|
|
116
116
|
"packages/provider-mail-resend/node_modules/resend",
|
|
117
117
|
),
|
|
118
118
|
"next-themes": path.join(repoRoot, "packages/cli/node_modules/next-themes"),
|
|
119
|
-
tsx: path.join(repoRoot, "node_modules/tsx"),
|
|
119
|
+
tsx: path.join(repoRoot, "packages/cli/node_modules/tsx"),
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
for (const packageName of externalPackages) {
|