teamshare-bridge 0.3.0 → 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 (123) 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 +9 -6
  11. package/dist/cli/commands/build.js.map +1 -1
  12. package/dist/cli/commands/doc.js +1 -1
  13. package/dist/cli/commands/doc.js.map +1 -1
  14. package/dist/cli/commands/run.js +3 -2
  15. package/dist/cli/commands/run.js.map +1 -1
  16. package/dist/cli/commands/scaffold.d.ts +1 -0
  17. package/dist/cli/commands/scaffold.js +111 -0
  18. package/dist/cli/commands/scaffold.js.map +1 -0
  19. package/dist/cli/commands/worker.d.ts +2 -0
  20. package/dist/cli/commands/worker.js +110 -0
  21. package/dist/cli/commands/worker.js.map +1 -0
  22. package/dist/cli/index.js +16 -0
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/lib/api.d.ts +22 -3
  25. package/dist/lib/api.js +9 -2
  26. package/dist/lib/api.js.map +1 -1
  27. package/dist/lib/audit/docs.d.ts +5 -0
  28. package/dist/lib/audit/docs.js +52 -0
  29. package/dist/lib/audit/docs.js.map +1 -0
  30. package/dist/lib/audit/errors.d.ts +15 -0
  31. package/dist/lib/audit/errors.js +154 -0
  32. package/dist/lib/audit/errors.js.map +1 -0
  33. package/dist/lib/audit/gates.d.ts +2 -0
  34. package/dist/lib/audit/gates.js +57 -0
  35. package/dist/lib/audit/gates.js.map +1 -0
  36. package/dist/lib/audit/modularity.d.ts +13 -0
  37. package/dist/lib/audit/modularity.js +200 -0
  38. package/dist/lib/audit/modularity.js.map +1 -0
  39. package/dist/lib/audit/quality.d.ts +9 -0
  40. package/dist/lib/audit/quality.js +128 -0
  41. package/dist/lib/audit/quality.js.map +1 -0
  42. package/dist/lib/audit/rbac.d.ts +14 -0
  43. package/dist/lib/audit/rbac.js +139 -0
  44. package/dist/lib/audit/rbac.js.map +1 -0
  45. package/dist/lib/audit/reporter.d.ts +5 -0
  46. package/dist/lib/audit/reporter.js +107 -0
  47. package/dist/lib/audit/reporter.js.map +1 -0
  48. package/dist/lib/audit/runner.d.ts +2 -0
  49. package/dist/lib/audit/runner.js +150 -0
  50. package/dist/lib/audit/runner.js.map +1 -0
  51. package/dist/lib/audit/security.d.ts +9 -0
  52. package/dist/lib/audit/security.js +151 -0
  53. package/dist/lib/audit/security.js.map +1 -0
  54. package/dist/lib/audit/types.d.ts +67 -0
  55. package/dist/lib/audit/types.js +26 -0
  56. package/dist/lib/audit/types.js.map +1 -0
  57. package/dist/lib/audit/ux.d.ts +9 -0
  58. package/dist/lib/audit/ux.js +112 -0
  59. package/dist/lib/audit/ux.js.map +1 -0
  60. package/dist/lib/config.d.ts +1 -1
  61. package/dist/lib/config.js +1 -1
  62. package/dist/lib/llm.js +1 -0
  63. package/dist/lib/llm.js.map +1 -1
  64. package/dist/lib/lock.d.ts +1 -1
  65. package/dist/lib/scaffold/renderer.d.ts +7 -0
  66. package/dist/lib/scaffold/renderer.js +37 -0
  67. package/dist/lib/scaffold/renderer.js.map +1 -0
  68. package/dist/lib/scaffold/structure.d.ts +3 -0
  69. package/dist/lib/scaffold/structure.js +73 -0
  70. package/dist/lib/scaffold/structure.js.map +1 -0
  71. package/dist/lib/scaffold/types.d.ts +34 -0
  72. package/dist/lib/scaffold/types.js +6 -0
  73. package/dist/lib/scaffold/types.js.map +1 -0
  74. package/dist/lib/scaffold/verify.d.ts +9 -0
  75. package/dist/lib/scaffold/verify.js +48 -0
  76. package/dist/lib/scaffold/verify.js.map +1 -0
  77. package/dist/lib/skills.d.ts +2 -2
  78. package/dist/lib/skills.js +2 -1
  79. package/dist/lib/skills.js.map +1 -1
  80. package/package.json +3 -2
  81. package/skills/teamshare-orchestrator-ask/SKILL.md +14 -0
  82. package/skills/teamshare-scaffold/SKILL.md +133 -0
  83. package/templates/backend/.env.example.tpl +13 -0
  84. package/templates/backend/nest-cli.json.tpl +8 -0
  85. package/templates/backend/package.json.tpl +41 -0
  86. package/templates/backend/prisma/schema.prisma.tpl +39 -0
  87. package/templates/backend/src/app.module.ts.tpl +24 -0
  88. package/templates/backend/src/common/filters/all-exceptions.filter.ts.tpl +59 -0
  89. package/templates/backend/src/common/guards/jwt-auth.guard.ts.tpl +27 -0
  90. package/templates/backend/src/common/guards/rbac.guard.ts.tpl +48 -0
  91. package/templates/backend/src/common/interceptors/transform.interceptor.ts.tpl +44 -0
  92. package/templates/backend/src/common/pipes/zod-validation.pipe.ts.tpl +29 -0
  93. package/templates/backend/src/common/swagger/envelope.schemas.ts.tpl +53 -0
  94. package/templates/backend/src/common/swagger/error.schemas.ts.tpl +62 -0
  95. package/templates/backend/src/main.ts.tpl +27 -0
  96. package/templates/backend/src/modules/example/example.controller.ts.tpl +102 -0
  97. package/templates/backend/src/modules/example/example.dto.ts.tpl +14 -0
  98. package/templates/backend/src/modules/example/example.module.ts.tpl +10 -0
  99. package/templates/backend/src/modules/example/example.service.ts.tpl +45 -0
  100. package/templates/backend/tsconfig.json.tpl +26 -0
  101. package/templates/common/.gitignore.tpl +42 -0
  102. package/templates/common/AGENTS.md.tpl +58 -0
  103. package/templates/common/docs/adr/README.md +25 -0
  104. package/templates/common/docs/api-contract.md.tpl +72 -0
  105. package/templates/web/next.config.ts.tpl +7 -0
  106. package/templates/web/package.json.tpl +37 -0
  107. package/templates/web/src/app/globals.css.tpl +48 -0
  108. package/templates/web/src/app/layout.tsx.tpl +25 -0
  109. package/templates/web/src/components/shared/app-button.tsx.tpl +59 -0
  110. package/templates/web/src/components/shared/app-empty-state.tsx.tpl +39 -0
  111. package/templates/web/src/components/shared/app-list-toolbar.tsx.tpl +63 -0
  112. package/templates/web/src/components/shared/app-skeleton.tsx.tpl +14 -0
  113. package/templates/web/src/components/shared/app-table.tsx.tpl +128 -0
  114. package/templates/web/src/components/shared/index.ts.tpl +5 -0
  115. package/templates/web/src/hooks/use-list-state.ts.tpl +72 -0
  116. package/templates/web/src/hooks/use-url-tab.ts.tpl +25 -0
  117. package/templates/web/src/lib/api/client.ts.tpl +123 -0
  118. package/templates/web/src/lib/providers.tsx.tpl +35 -0
  119. package/templates/web/src/lib/query-keys.ts.tpl +21 -0
  120. package/templates/web/src/lib/utils.ts.tpl +7 -0
  121. package/templates/web/src/lib/validation/schemas.ts.tpl +15 -0
  122. package/templates/web/src/modules/example/views/example-list-view.tsx.tpl +98 -0
  123. package/templates/web/tsconfig.json.tpl +23 -0
@@ -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.
@@ -0,0 +1,72 @@
1
+ # API Contract - {{projectName}}
2
+
3
+ ## Response envelope
4
+
5
+ Every successful response returns:
6
+
7
+ ```json
8
+ {
9
+ "success": true,
10
+ "data": { ... }
11
+ }
12
+ ```
13
+
14
+ Paginated responses include a `pagination` field:
15
+
16
+ ```json
17
+ {
18
+ "success": true,
19
+ "data": [ ... ],
20
+ "pagination": {
21
+ "page": 1,
22
+ "limit": 20,
23
+ "total": 100,
24
+ "totalPages": 5
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Error envelope
30
+
31
+ ```json
32
+ {
33
+ "success": false,
34
+ "error": {
35
+ "code": "NOT_FOUND",
36
+ "message": "Resource not found",
37
+ "details": null
38
+ }
39
+ }
40
+ ```
41
+
42
+ Common error codes: `BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `UNPROCESSABLE_ENTITY`, `TOO_MANY_REQUESTS`, `INTERNAL_ERROR`.
43
+
44
+ ## Authentication
45
+
46
+ - Bearer token in `Authorization` header
47
+ - JWT issued by `/api/auth/login`
48
+ - Public routes marked with `@Public()` decorator
49
+
50
+ ## RBAC
51
+
52
+ Route permissions declared via `@Permissions("resource:action")` decorator.
53
+ Roles resolve against the addressed company scope.
54
+
55
+ | Role | Description |
56
+ |---|---|
57
+ | owner | Full access |
58
+ | admin | Manage members, settings |
59
+ | member | Standard access |
60
+ | viewer | Read-only |
61
+
62
+ ## Pagination
63
+
64
+ Query params: `page` (default 1), `limit` (default 20, max 100).
65
+
66
+ ## Search
67
+
68
+ `search` query param performs case-insensitive text matching.
69
+
70
+ ## Sorting
71
+
72
+ `sort` and `order` query params (e.g. `?sort=createdAt&order=desc`).
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ reactStrictMode: true,
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "{{description}}",
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint"
11
+ },
12
+ "dependencies": {
13
+ "next": "^16.0.0",
14
+ "react": "^19.0.0",
15
+ "react-dom": "^19.0.0",
16
+ "next-auth": "^4.24.0",
17
+ "next-themes": "^0.4.0",
18
+ "@tanstack/react-query": "^5.60.0",
19
+ "@tanstack/react-table": "^8.20.0",
20
+ "react-hook-form": "^7.53.0",
21
+ "@hookform/resolvers": "^3.9.0",
22
+ "zod": "^3.23.0",
23
+ "socket.io-client": "^4.8.0",
24
+ "iconsax-react": "^0.0.8",
25
+ "clsx": "^2.1.0",
26
+ "tailwind-merge": "^2.5.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.0.0",
30
+ "@types/react": "^19.0.0",
31
+ "@types/react-dom": "^19.0.0",
32
+ "typescript": "^5.6.0",
33
+ "tailwindcss": "^4.0.0",
34
+ "@tailwindcss/postcss": "^4.0.0",
35
+ "postcss": "^8.4.0"
36
+ }
37
+ }
@@ -0,0 +1,48 @@
1
+ @import "tailwindcss";
2
+
3
+ @theme inline {
4
+ --app-primary-50: #eff6ff;
5
+ --app-primary-100: #dbeafe;
6
+ --app-primary-200: #bfdbfe;
7
+ --app-primary-300: #93c5fd;
8
+ --app-primary-400: #60a5fa;
9
+ --app-primary-500: #3b82f6;
10
+ --app-primary-600: #2563eb;
11
+ --app-primary-700: #1d4ed8;
12
+ --app-primary-800: #1e40af;
13
+ --app-primary-900: #1e3a8a;
14
+
15
+ --app-bg-primary: #ffffff;
16
+ --app-bg-secondary: #f8fafc;
17
+ --app-bg-tertiary: #f1f5f9;
18
+ --app-bg-inverse: #0f172a;
19
+
20
+ --app-text-primary: #0f172a;
21
+ --app-text-secondary: #475569;
22
+ --app-text-tertiary: #94a3b8;
23
+ --app-text-inverse: #f8fafc;
24
+
25
+ --app-border-default: #e2e8f0;
26
+ --app-border-strong: #cbd5e1;
27
+
28
+ --app-radius: 0.5rem;
29
+
30
+ --color-background: var(--app-bg-primary);
31
+ --color-foreground: var(--app-text-primary);
32
+ --color-muted: var(--app-bg-secondary);
33
+ --color-muted-foreground: var(--app-text-secondary);
34
+ --color-border: var(--app-border-default);
35
+ --color-primary: var(--app-primary-600);
36
+ --color-primary-foreground: var(--app-text-inverse);
37
+ }
38
+
39
+ @custom-variant dark (&:is(.dark *));
40
+
41
+ @layer base {
42
+ * {
43
+ @apply border-border;
44
+ }
45
+ body {
46
+ @apply bg-background text-foreground;
47
+ }
48
+ }
@@ -0,0 +1,25 @@
1
+ import type { Metadata } from "next";
2
+ import { Inter } from "next/font/google";
3
+ import { Providers } from "@/lib/providers";
4
+ import "./globals.css";
5
+
6
+ const inter = Inter({ subsets: ["latin"] });
7
+
8
+ export const metadata: Metadata = {
9
+ title: "{{projectName}}",
10
+ description: "{{description}}",
11
+ };
12
+
13
+ export default function RootLayout({
14
+ children,
15
+ }: {
16
+ children: React.ReactNode;
17
+ }) {
18
+ return (
19
+ <html lang="en" suppressHydrationWarning>
20
+ <body className={inter.className}>
21
+ <Providers>{children}</Providers>
22
+ </body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,59 @@
1
+ import { Loader2 } from "lucide-react";
2
+ import { type ButtonHTMLAttributes, forwardRef } from "react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 hover:underline",
20
+ },
21
+ size: {
22
+ default: "h-10 px-4 py-2",
23
+ sm: "h-9 rounded-md px-3",
24
+ lg: "h-11 rounded-md px-8",
25
+ icon: "h-10 w-10",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ size: "default",
31
+ },
32
+ }
33
+ );
34
+
35
+ export interface AppButtonProps
36
+ extends ButtonHTMLAttributes<HTMLButtonElement>,
37
+ VariantProps<typeof buttonVariants> {
38
+ loading?: boolean;
39
+ }
40
+
41
+ const AppButton = forwardRef<HTMLButtonElement, AppButtonProps>(
42
+ ({ className, variant, size, loading, disabled, children, ...props }, ref) => {
43
+ return (
44
+ <button
45
+ ref={ref}
46
+ data-slot="app-button"
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ disabled={disabled || loading}
49
+ {...props}
50
+ >
51
+ {loading && <Loader2 className="h-4 w-4 animate-spin" variant="Outline" color="currentColor" />}
52
+ {children}
53
+ </button>
54
+ );
55
+ }
56
+ );
57
+ AppButton.displayName = "AppButton";
58
+
59
+ export { AppButton, buttonVariants };