teamshare-bridge 0.2.5 → 0.4.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 (140) hide show
  1. package/dist/bridge/daemon.js +36 -0
  2. package/dist/bridge/daemon.js.map +1 -1
  3. package/dist/bridge/local-server.d.ts +1 -1
  4. package/dist/bridge/spawn.d.ts +2 -0
  5. package/dist/bridge/spawn.js +17 -0
  6. package/dist/bridge/spawn.js.map +1 -1
  7. package/dist/cli/commands/audit.d.ts +1 -0
  8. package/dist/cli/commands/audit.js +75 -0
  9. package/dist/cli/commands/audit.js.map +1 -0
  10. package/dist/cli/commands/build.js +24 -8
  11. package/dist/cli/commands/build.js.map +1 -1
  12. package/dist/cli/commands/chat.js +1 -1
  13. package/dist/cli/commands/chat.js.map +1 -1
  14. package/dist/cli/commands/doc.js +2 -2
  15. package/dist/cli/commands/doc.js.map +1 -1
  16. package/dist/cli/commands/harness.js +1 -1
  17. package/dist/cli/commands/harness.js.map +1 -1
  18. package/dist/cli/commands/run.js +19 -4
  19. package/dist/cli/commands/run.js.map +1 -1
  20. package/dist/cli/commands/scaffold.d.ts +1 -0
  21. package/dist/cli/commands/scaffold.js +111 -0
  22. package/dist/cli/commands/scaffold.js.map +1 -0
  23. package/dist/cli/commands/worker.d.ts +2 -0
  24. package/dist/cli/commands/worker.js +110 -0
  25. package/dist/cli/commands/worker.js.map +1 -0
  26. package/dist/cli/index.js +16 -0
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/lib/api.d.ts +35 -4
  29. package/dist/lib/api.js +20 -3
  30. package/dist/lib/api.js.map +1 -1
  31. package/dist/lib/audit/docs.d.ts +5 -0
  32. package/dist/lib/audit/docs.js +52 -0
  33. package/dist/lib/audit/docs.js.map +1 -0
  34. package/dist/lib/audit/errors.d.ts +15 -0
  35. package/dist/lib/audit/errors.js +154 -0
  36. package/dist/lib/audit/errors.js.map +1 -0
  37. package/dist/lib/audit/gates.d.ts +2 -0
  38. package/dist/lib/audit/gates.js +57 -0
  39. package/dist/lib/audit/gates.js.map +1 -0
  40. package/dist/lib/audit/modularity.d.ts +13 -0
  41. package/dist/lib/audit/modularity.js +200 -0
  42. package/dist/lib/audit/modularity.js.map +1 -0
  43. package/dist/lib/audit/quality.d.ts +9 -0
  44. package/dist/lib/audit/quality.js +128 -0
  45. package/dist/lib/audit/quality.js.map +1 -0
  46. package/dist/lib/audit/rbac.d.ts +14 -0
  47. package/dist/lib/audit/rbac.js +139 -0
  48. package/dist/lib/audit/rbac.js.map +1 -0
  49. package/dist/lib/audit/reporter.d.ts +5 -0
  50. package/dist/lib/audit/reporter.js +107 -0
  51. package/dist/lib/audit/reporter.js.map +1 -0
  52. package/dist/lib/audit/runner.d.ts +2 -0
  53. package/dist/lib/audit/runner.js +150 -0
  54. package/dist/lib/audit/runner.js.map +1 -0
  55. package/dist/lib/audit/security.d.ts +9 -0
  56. package/dist/lib/audit/security.js +151 -0
  57. package/dist/lib/audit/security.js.map +1 -0
  58. package/dist/lib/audit/types.d.ts +67 -0
  59. package/dist/lib/audit/types.js +26 -0
  60. package/dist/lib/audit/types.js.map +1 -0
  61. package/dist/lib/audit/ux.d.ts +9 -0
  62. package/dist/lib/audit/ux.js +112 -0
  63. package/dist/lib/audit/ux.js.map +1 -0
  64. package/dist/lib/brief.d.ts +4 -0
  65. package/dist/lib/brief.js +78 -40
  66. package/dist/lib/brief.js.map +1 -1
  67. package/dist/lib/chat-reply.js +2 -2
  68. package/dist/lib/chat-reply.js.map +1 -1
  69. package/dist/lib/config.d.ts +1 -1
  70. package/dist/lib/config.js +1 -1
  71. package/dist/lib/doc-reply.js +1 -1
  72. package/dist/lib/doc-reply.js.map +1 -1
  73. package/dist/lib/harness-adapter.d.ts +3 -1
  74. package/dist/lib/harness-adapter.js +19 -16
  75. package/dist/lib/harness-adapter.js.map +1 -1
  76. package/dist/lib/harness.d.ts +3 -1
  77. package/dist/lib/harness.js +8 -5
  78. package/dist/lib/harness.js.map +1 -1
  79. package/dist/lib/llm.js +87 -9
  80. package/dist/lib/llm.js.map +1 -1
  81. package/dist/lib/lock.d.ts +1 -1
  82. package/dist/lib/scaffold/renderer.d.ts +7 -0
  83. package/dist/lib/scaffold/renderer.js +37 -0
  84. package/dist/lib/scaffold/renderer.js.map +1 -0
  85. package/dist/lib/scaffold/structure.d.ts +3 -0
  86. package/dist/lib/scaffold/structure.js +73 -0
  87. package/dist/lib/scaffold/structure.js.map +1 -0
  88. package/dist/lib/scaffold/types.d.ts +34 -0
  89. package/dist/lib/scaffold/types.js +6 -0
  90. package/dist/lib/scaffold/types.js.map +1 -0
  91. package/dist/lib/scaffold/verify.d.ts +9 -0
  92. package/dist/lib/scaffold/verify.js +48 -0
  93. package/dist/lib/scaffold/verify.js.map +1 -0
  94. package/dist/lib/skills.d.ts +2 -2
  95. package/dist/lib/skills.js +2 -1
  96. package/dist/lib/skills.js.map +1 -1
  97. package/package.json +3 -2
  98. package/skills/teamshare-orchestrator-ask/SKILL.md +14 -0
  99. package/skills/teamshare-scaffold/SKILL.md +133 -0
  100. package/templates/backend/.env.example.tpl +13 -0
  101. package/templates/backend/nest-cli.json.tpl +8 -0
  102. package/templates/backend/package.json.tpl +41 -0
  103. package/templates/backend/prisma/schema.prisma.tpl +39 -0
  104. package/templates/backend/src/app.module.ts.tpl +24 -0
  105. package/templates/backend/src/common/filters/all-exceptions.filter.ts.tpl +59 -0
  106. package/templates/backend/src/common/guards/jwt-auth.guard.ts.tpl +27 -0
  107. package/templates/backend/src/common/guards/rbac.guard.ts.tpl +48 -0
  108. package/templates/backend/src/common/interceptors/transform.interceptor.ts.tpl +44 -0
  109. package/templates/backend/src/common/pipes/zod-validation.pipe.ts.tpl +29 -0
  110. package/templates/backend/src/common/swagger/envelope.schemas.ts.tpl +53 -0
  111. package/templates/backend/src/common/swagger/error.schemas.ts.tpl +62 -0
  112. package/templates/backend/src/main.ts.tpl +27 -0
  113. package/templates/backend/src/modules/example/example.controller.ts.tpl +102 -0
  114. package/templates/backend/src/modules/example/example.dto.ts.tpl +14 -0
  115. package/templates/backend/src/modules/example/example.module.ts.tpl +10 -0
  116. package/templates/backend/src/modules/example/example.service.ts.tpl +45 -0
  117. package/templates/backend/tsconfig.json.tpl +26 -0
  118. package/templates/common/.gitignore.tpl +42 -0
  119. package/templates/common/AGENTS.md.tpl +58 -0
  120. package/templates/common/docs/adr/README.md +25 -0
  121. package/templates/common/docs/api-contract.md.tpl +72 -0
  122. package/templates/web/next.config.ts.tpl +7 -0
  123. package/templates/web/package.json.tpl +37 -0
  124. package/templates/web/src/app/globals.css.tpl +48 -0
  125. package/templates/web/src/app/layout.tsx.tpl +25 -0
  126. package/templates/web/src/components/shared/app-button.tsx.tpl +59 -0
  127. package/templates/web/src/components/shared/app-empty-state.tsx.tpl +39 -0
  128. package/templates/web/src/components/shared/app-list-toolbar.tsx.tpl +63 -0
  129. package/templates/web/src/components/shared/app-skeleton.tsx.tpl +14 -0
  130. package/templates/web/src/components/shared/app-table.tsx.tpl +128 -0
  131. package/templates/web/src/components/shared/index.ts.tpl +5 -0
  132. package/templates/web/src/hooks/use-list-state.ts.tpl +72 -0
  133. package/templates/web/src/hooks/use-url-tab.ts.tpl +25 -0
  134. package/templates/web/src/lib/api/client.ts.tpl +123 -0
  135. package/templates/web/src/lib/providers.tsx.tpl +35 -0
  136. package/templates/web/src/lib/query-keys.ts.tpl +21 -0
  137. package/templates/web/src/lib/utils.ts.tpl +7 -0
  138. package/templates/web/src/lib/validation/schemas.ts.tpl +15 -0
  139. package/templates/web/src/modules/example/views/example-list-view.tsx.tpl +98 -0
  140. package/templates/web/tsconfig.json.tpl +23 -0
@@ -0,0 +1,27 @@
1
+ import { ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
2
+ import { Reflector } from "@nestjs/core";
3
+ import { AuthGuard as PassportAuthGuard } from "@nestjs/passport";
4
+ import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
5
+
6
+ @Injectable()
7
+ export class AuthGuard extends PassportAuthGuard("jwt") {
8
+ constructor(private reflector: Reflector) {
9
+ super();
10
+ }
11
+
12
+ canActivate(context: ExecutionContext) {
13
+ const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
14
+ context.getHandler(),
15
+ context.getClass(),
16
+ ]);
17
+ if (isPublic) return true;
18
+ return super.canActivate(context);
19
+ }
20
+
21
+ handleRequest(err: unknown, user: unknown) {
22
+ if (err || !user) {
23
+ throw err ?? new UnauthorizedException("Invalid or missing token");
24
+ }
25
+ return user;
26
+ }
27
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ CanActivate,
3
+ ExecutionContext,
4
+ ForbiddenException,
5
+ Injectable,
6
+ } from "@nestjs/common";
7
+ import { Reflector } from "@nestjs/core";
8
+ import { PERMISSIONS_KEY } from "../decorators/permissions.decorator";
9
+ import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
10
+
11
+ @Injectable()
12
+ export class RbacGuard implements CanActivate {
13
+ constructor(private reflector: Reflector) {}
14
+
15
+ canActivate(context: ExecutionContext): boolean {
16
+ const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
17
+ context.getHandler(),
18
+ context.getClass(),
19
+ ]);
20
+ if (isPublic) return true;
21
+
22
+ const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
23
+ PERMISSIONS_KEY,
24
+ [context.getHandler(), context.getClass()]
25
+ );
26
+ if (!requiredPermissions || requiredPermissions.length === 0) return true;
27
+
28
+ const request = context.switchToHttp().getRequest();
29
+ const user = request.user;
30
+
31
+ if (!user) {
32
+ throw new ForbiddenException("Authentication required");
33
+ }
34
+
35
+ const userPermissions: string[] = user.permissions ?? [];
36
+ const hasPermission = requiredPermissions.every((p) =>
37
+ userPermissions.includes(p)
38
+ );
39
+
40
+ if (!hasPermission) {
41
+ throw new ForbiddenException(
42
+ `Missing required permissions: ${requiredPermissions.join(", ")}`
43
+ );
44
+ }
45
+
46
+ return true;
47
+ }
48
+ }
@@ -0,0 +1,44 @@
1
+ import {
2
+ Injectable,
3
+ NestInterceptor,
4
+ ExecutionContext,
5
+ CallHandler,
6
+ } from "@nestjs/common";
7
+ import { Observable, map } from "rxjs";
8
+
9
+ export interface SuccessResponse<T> {
10
+ success: true;
11
+ data: T;
12
+ pagination?: {
13
+ page: number;
14
+ limit: number;
15
+ total: number;
16
+ totalPages: number;
17
+ };
18
+ }
19
+
20
+ @Injectable()
21
+ export class TransformInterceptor<T>
22
+ implements NestInterceptor<T, SuccessResponse<T>>
23
+ {
24
+ intercept(
25
+ context: ExecutionContext,
26
+ next: CallHandler
27
+ ): Observable<SuccessResponse<T>> {
28
+ return next.handle().pipe(
29
+ map((data) => {
30
+ if (data && typeof data === "object" && "data" in data && "pagination" in data) {
31
+ return {
32
+ success: true as const,
33
+ data: data.data,
34
+ pagination: data.pagination,
35
+ };
36
+ }
37
+ return {
38
+ success: true as const,
39
+ data,
40
+ };
41
+ })
42
+ );
43
+ }
44
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ PipeTransform,
3
+ Injectable,
4
+ BadRequestException,
5
+ } from "@nestjs/common";
6
+ import { ZodSchema } from "zod";
7
+
8
+ @Injectable()
9
+ export class ZodValidationPipe implements PipeTransform {
10
+ constructor(
11
+ private schema: ZodSchema,
12
+ private source: "body" | "query" | "params" = "body"
13
+ ) {}
14
+
15
+ transform(value: unknown) {
16
+ const result = this.schema.safeParse(value);
17
+ if (!result.success) {
18
+ const issues = result.error.issues.map((i) => ({
19
+ path: i.path.join("."),
20
+ message: i.message,
21
+ }));
22
+ throw new BadRequestException({
23
+ message: "Validation failed",
24
+ details: issues,
25
+ });
26
+ }
27
+ return result.data;
28
+ }
29
+ }
@@ -0,0 +1,53 @@
1
+
2
+ export function successSchema(dataSchema: Record<string, unknown>) {
3
+ return {
4
+ type: "object",
5
+ properties: {
6
+ success: { type: "boolean", example: true },
7
+ data: dataSchema,
8
+ },
9
+ required: ["success", "data"],
10
+ };
11
+ }
12
+
13
+ export function paginatedSchema(dataSchema: Record<string, unknown>) {
14
+ return {
15
+ type: "object",
16
+ properties: {
17
+ success: { type: "boolean", example: true },
18
+ data: {
19
+ type: "array",
20
+ items: dataSchema,
21
+ },
22
+ pagination: {
23
+ type: "object",
24
+ properties: {
25
+ page: { type: "number", example: 1 },
26
+ limit: { type: "number", example: 20 },
27
+ total: { type: "number", example: 100 },
28
+ totalPages: { type: "number", example: 5 },
29
+ },
30
+ },
31
+ },
32
+ required: ["success", "data", "pagination"],
33
+ };
34
+ }
35
+
36
+ export function errorSchema() {
37
+ return {
38
+ type: "object",
39
+ properties: {
40
+ success: { type: "boolean", example: false },
41
+ error: {
42
+ type: "object",
43
+ properties: {
44
+ code: { type: "string", example: "NOT_FOUND" },
45
+ message: { type: "string", example: "Resource not found" },
46
+ details: {},
47
+ },
48
+ required: ["code", "message"],
49
+ },
50
+ },
51
+ required: ["success", "error"],
52
+ };
53
+ }
@@ -0,0 +1,62 @@
1
+ import { applyDecorators } from "@nestjs/common";
2
+ import { ApiResponse } from "@nestjs/swagger";
3
+
4
+ export const INTERNAL_ERROR_CODES = [
5
+ "INTERNAL_ERROR",
6
+ "BAD_REQUEST",
7
+ "UNAUTHORIZED",
8
+ "FORBIDDEN",
9
+ "NOT_FOUND",
10
+ "CONFLICT",
11
+ "UNPROCESSABLE_ENTITY",
12
+ "TOO_MANY_REQUESTS",
13
+ ] as const;
14
+
15
+ export function errs(...codes: (typeof INTERNAL_ERROR_CODES)[number][]) {
16
+ const descriptions: Record<string, string> = {
17
+ INTERNAL_ERROR: "Internal server error",
18
+ BAD_REQUEST: "Invalid request parameters",
19
+ UNAUTHORIZED: "Authentication required",
20
+ FORBIDDEN: "Insufficient permissions",
21
+ NOT_FOUND: "Resource not found",
22
+ CONFLICT: "Resource already exists",
23
+ UNPROCESSABLE_ENTITY: "Validation failed",
24
+ TOO_MANY_REQUESTS: "Rate limit exceeded",
25
+ };
26
+
27
+ return applyDecorators(
28
+ ...codes.map((code) =>
29
+ ApiResponse({
30
+ status: codeToStatus(code),
31
+ description: descriptions[code] ?? code,
32
+ schema: {
33
+ type: "object",
34
+ properties: {
35
+ success: { type: "boolean", example: false },
36
+ error: {
37
+ type: "object",
38
+ properties: {
39
+ code: { type: "string", example: code },
40
+ message: { type: "string", example: descriptions[code] },
41
+ },
42
+ },
43
+ },
44
+ },
45
+ })
46
+ )
47
+ );
48
+ }
49
+
50
+ function codeToStatus(code: string): number {
51
+ const map: Record<string, number> = {
52
+ BAD_REQUEST: 400,
53
+ UNAUTHORIZED: 401,
54
+ FORBIDDEN: 403,
55
+ NOT_FOUND: 404,
56
+ CONFLICT: 409,
57
+ UNPROCESSABLE_ENTITY: 422,
58
+ TOO_MANY_REQUESTS: 429,
59
+ INTERNAL_ERROR: 500,
60
+ };
61
+ return map[code] ?? 500;
62
+ }
@@ -0,0 +1,27 @@
1
+ import { NestFactory } from "@nestjs/core";
2
+ import { ValidationPipe } from "@nestjs/common";
3
+ import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
4
+ import { AppModule } from "./app.module";
5
+
6
+ async function bootstrap() {
7
+ const app = await NestFactory.create(AppModule);
8
+
9
+ app.setGlobalPrefix("api");
10
+ app.enableCors();
11
+ app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
12
+
13
+ const config = new DocumentBuilder()
14
+ .setTitle("{{projectName}} API")
15
+ .setDescription("{{description}}")
16
+ .setVersion("0.1.0")
17
+ .addBearerAuth()
18
+ .build();
19
+
20
+ const document = SwaggerModule.createDocument(app, config);
21
+ SwaggerModule.setup("docs", app, document);
22
+
23
+ await app.listen({{apiPort}});
24
+ console.log(`Application running on: http://localhost:{{apiPort}}`);
25
+ console.log(`Swagger docs: http://localhost:{{apiPort}}/docs`);
26
+ }
27
+ bootstrap();
@@ -0,0 +1,102 @@
1
+ import {
2
+ Controller,
3
+ Get,
4
+ Post,
5
+ Param,
6
+ Query,
7
+ Body,
8
+ ParseUUIDPipe,
9
+ } from "@nestjs/common";
10
+ import {
11
+ ApiTags,
12
+ ApiOperation,
13
+ ApiParam,
14
+ ApiQuery,
15
+ ApiBody,
16
+ ApiOkResponse,
17
+ ApiBearerAuth,
18
+ } from "@nestjs/swagger";
19
+ import { Permissions } from "../../common/decorators/permissions.decorator";
20
+ import { Public } from "../../common/decorators/public.decorator";
21
+ import { ZodValidationPipe } from "../../common/pipes/zod-validation.pipe";
22
+ import { successSchema, paginatedSchema } from "../../common/swagger/envelope.schemas";
23
+ import { errs } from "../../common/swagger/error.schemas";
24
+ import { ExampleService } from "./example.service";
25
+ import { CreateExampleSchema, ExampleQuerySchema } from "./example.dto";
26
+
27
+ @ApiTags("examples")
28
+ @ApiBearerAuth()
29
+ @Controller("examples")
30
+ export class ExampleController {
31
+ constructor(private readonly exampleService: ExampleService) {}
32
+
33
+ @Get()
34
+ @Public()
35
+ @ApiOperation({ summary: "List examples", description: "Retrieve a paginated list of examples" })
36
+ @ApiQuery({ name: "page", required: false, example: 1 })
37
+ @ApiQuery({ name: "limit", required: false, example: 20 })
38
+ @ApiQuery({ name: "search", required: false })
39
+ @ApiOkResponse({
40
+ description: "Paginated list of examples",
41
+ schema: paginatedSchema({
42
+ type: "object",
43
+ properties: {
44
+ id: { type: "string", format: "uuid" },
45
+ name: { type: "string" },
46
+ createdAt: { type: "string", format: "date-time" },
47
+ },
48
+ }),
49
+ })
50
+ @errs("INTERNAL_ERROR")
51
+ async list(
52
+ @Query(new ZodValidationPipe(ExampleQuerySchema, "query")) query: {
53
+ page: number;
54
+ limit: number;
55
+ search?: string;
56
+ }
57
+ ) {
58
+ return this.exampleService.list(query);
59
+ }
60
+
61
+ @Post()
62
+ @Permissions("example:create")
63
+ @ApiOperation({ summary: "Create example", description: "Create a new example" })
64
+ @ApiBody({ schema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] } })
65
+ @ApiOkResponse({
66
+ description: "Created example",
67
+ schema: successSchema({
68
+ type: "object",
69
+ properties: {
70
+ id: { type: "string", format: "uuid" },
71
+ name: { type: "string" },
72
+ createdAt: { type: "string", format: "date-time" },
73
+ },
74
+ }),
75
+ })
76
+ @errs("BAD_REQUEST", "UNAUTHORIZED", "FORBIDDEN", "INTERNAL_ERROR")
77
+ async create(
78
+ @Body(new ZodValidationPipe(CreateExampleSchema)) body: { name: string }
79
+ ) {
80
+ return this.exampleService.create(body);
81
+ }
82
+
83
+ @Get(":id")
84
+ @Permissions("example:read")
85
+ @ApiOperation({ summary: "Get example", description: "Get an example by ID" })
86
+ @ApiParam({ name: "id", required: true })
87
+ @ApiOkResponse({
88
+ description: "Example details",
89
+ schema: successSchema({
90
+ type: "object",
91
+ properties: {
92
+ id: { type: "string", format: "uuid" },
93
+ name: { type: "string" },
94
+ createdAt: { type: "string", format: "date-time" },
95
+ },
96
+ }),
97
+ })
98
+ @errs("NOT_FOUND", "UNAUTHORIZED", "FORBIDDEN", "INTERNAL_ERROR")
99
+ async getOne(@Param("id", ParseUUIDPipe) id: string) {
100
+ return this.exampleService.getOne(id);
101
+ }
102
+ }
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+
3
+ export const CreateExampleSchema = z.object({
4
+ name: z.string().min(1).max(200),
5
+ });
6
+
7
+ export const ExampleQuerySchema = z.object({
8
+ page: z.coerce.number().int().positive().default(1),
9
+ limit: z.coerce.number().int().positive().max(100).default(20),
10
+ search: z.string().optional(),
11
+ });
12
+
13
+ export type CreateExampleInput = z.infer<typeof CreateExampleSchema>;
14
+ export type ExampleQueryInput = z.infer<typeof ExampleQuerySchema>;
@@ -0,0 +1,10 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { ExampleController } from "./example.controller";
3
+ import { ExampleService } from "./example.service";
4
+
5
+ @Module({
6
+ controllers: [ExampleController],
7
+ providers: [ExampleService],
8
+ exports: [ExampleService],
9
+ })
10
+ export class ExampleModule {}
@@ -0,0 +1,45 @@
1
+ import { Injectable, NotFoundException } from "@nestjs/common";
2
+ import { PrismaService } from "../../prisma/prisma.service";
3
+
4
+ @Injectable()
5
+ export class ExampleService {
6
+ constructor(private readonly prisma: PrismaService) {}
7
+
8
+ async list(query: { page: number; limit: number; search?: string }) {
9
+ const where = query.search
10
+ ? { name: { contains: query.search, mode: "insensitive" as const } }
11
+ : {};
12
+
13
+ const [data, total] = await Promise.all([
14
+ this.prisma.example.findMany({
15
+ where,
16
+ skip: (query.page - 1) * query.limit,
17
+ take: query.limit,
18
+ orderBy: { createdAt: "desc" },
19
+ }),
20
+ this.prisma.example.count({ where }),
21
+ ]);
22
+
23
+ return {
24
+ data,
25
+ pagination: {
26
+ page: query.page,
27
+ limit: query.limit,
28
+ total,
29
+ totalPages: Math.ceil(total / query.limit),
30
+ },
31
+ };
32
+ }
33
+
34
+ async create(data: { name: string }) {
35
+ return this.prisma.example.create({ data });
36
+ }
37
+
38
+ async getOne(id: string) {
39
+ const example = await this.prisma.example.findUnique({ where: { id } });
40
+ if (!example) {
41
+ throw new NotFoundException("Example not found");
42
+ }
43
+ return example;
44
+ }
45
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "declaration": true,
5
+ "removeComments": true,
6
+ "emitDecoratorMetadata": true,
7
+ "experimentalDecorators": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "target": "ES2021",
10
+ "sourceMap": true,
11
+ "outDir": "./dist",
12
+ "baseUrl": "./",
13
+ "incremental": true,
14
+ "skipLibCheck": true,
15
+ "strictNullChecks": true,
16
+ "noImplicitAny": false,
17
+ "strictBindCallApply": false,
18
+ "forceConsistentCasingInFileNames": false,
19
+ "noFallthroughCasesInSwitch": false,
20
+ "paths": {
21
+ "@/*": ["src/*"]
22
+ }
23
+ },
24
+ "include": ["src/**/*"],
25
+ "exclude": ["node_modules", "dist"]
26
+ }
@@ -0,0 +1,42 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build output
5
+ dist/
6
+ .next/
7
+ out/
8
+
9
+ # Environment
10
+ .env
11
+ .env.local
12
+ .env.*.local
13
+
14
+ # IDE
15
+ .vscode/
16
+ .idea/
17
+ *.swp
18
+ *.swo
19
+ *~
20
+
21
+ # OS
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+ # Debug
26
+ npm-debug.log*
27
+ yarn-debug.log*
28
+ yarn-error.log*
29
+
30
+ # Prisma
31
+ prisma/migrations/
32
+
33
+ # TypeScript
34
+ *.tsbuildinfo
35
+ next-env.d.ts
36
+
37
+ # Logs
38
+ *.log
39
+
40
+ # Temp
41
+ tmp/
42
+ temp/
@@ -0,0 +1,58 @@
1
+ # AGENTS.md - {{projectName}}
2
+
3
+ ## What this is
4
+
5
+ {{description}}
6
+
7
+ ## Tech stack
8
+
9
+ - **Frontend:** Next.js 16, React 19, TypeScript, Tailwind v4, shadcn, react-query v5, TanStack Table v8, react-hook-form, zod, next-auth v4, next-themes, socket.io-client
10
+ - **Backend:** NestJS 11, Prisma 6, zod, Swagger/OpenAPI, passport-jwt, argon2, socket.io
11
+ - **Database:** PostgreSQL
12
+
13
+ ## Folder structure
14
+
15
+ ```
16
+ {{slug}}/
17
+ ├── frontend/ # Next.js web app
18
+ │ ├── src/
19
+ │ │ ├── app/ # App Router routes
20
+ │ │ ├── components/
21
+ │ │ │ ├── ui/ # Vendor primitives (shadcn) — do not edit
22
+ │ │ │ └── shared/# Shared wrappers (AppButton, AppTable, etc.)
23
+ │ │ ├── hooks/ # Custom React hooks
24
+ │ │ ├── lib/ # Utilities, API client, query keys, validation
25
+ │ │ └── modules/ # Feature modules (views, components, columns)
26
+ │ └── package.json
27
+ ├── backend/ # NestJS API
28
+ │ ├── prisma/ # Schema + migrations
29
+ │ ├── src/
30
+ │ │ ├── common/ # Guards, filters, interceptors, pipes, swagger
31
+ │ │ ├── modules/ # Feature modules (controller, service, dto, module)
32
+ │ │ ├── generated/ # Prisma + zod generated output
33
+ │ │ └── main.ts
34
+ │ └── package.json
35
+ └── docs/ # Project documentation
36
+ ```
37
+
38
+ ## Build commands
39
+
40
+ | Command | Location | Description |
41
+ |---|---|---|
42
+ | `npm run dev` | frontend/ | Start Next.js dev server |
43
+ | `npm run build` | frontend/ | Build Next.js app |
44
+ | `npm run dev` | backend/ | Start NestJS dev server |
45
+ | `npm run build` | backend/ | Build NestJS app |
46
+ | `npx prisma generate` | backend/ | Regenerate Prisma client |
47
+ | `npx prisma migrate dev` | backend/ | Run migrations |
48
+
49
+ ## Conventions
50
+
51
+ - Shared components use a consistent prefix (e.g., App*, or your own choice)
52
+ - Use `data-slot="app-*"` attribute on shared components for styling hooks
53
+ - Backend uses ZodValidationPipe for request validation
54
+ - All endpoints return `{ success: true, data }` envelope
55
+ - RBAC via `@Permissions()` decorator, `@Public()` for open routes
56
+ - Query keys live in `src/lib/query-keys.ts`
57
+ - Validation schemas live in `src/lib/validation/schemas.ts`
58
+ - Loading states: every query view has skeleton + error + empty state
@@ -0,0 +1,25 @@
1
+ # Architecture Decision Records
2
+
3
+ One file per decision, named `NNN-short-title.md` (e.g. `001-orm-choice.md`).
4
+
5
+ Format for each ADR:
6
+
7
+ ```markdown
8
+ # ADR-NNN: <Title>
9
+
10
+ **Status:** proposed | accepted | superseded by ADR-MMM
11
+ **Date:** YYYY-MM-DD
12
+
13
+ ## Context
14
+ What problem or force drove this decision?
15
+
16
+ ## Decision
17
+ What was decided, stated plainly.
18
+
19
+ ## Consequences
20
+ What becomes easier, what becomes harder, what must now be true.
21
+ ```
22
+
23
+ Write an ADR whenever a choice is expensive to reverse (ORM, validation
24
+ strategy, folder layout, auth model). Agents working in this repo should
25
+ read existing ADRs before proposing conflicting changes.