@torus-engineering/tas-kit 1.11.1 → 1.13.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/.tas/README.md +334 -334
  2. package/{.claude → .tas/_platform/claude-code}/settings.json +0 -12
  3. package/{.claude → .tas/_platform}/hooks/code-quality.js +1 -1
  4. package/{.claude → .tas/_platform}/hooks/session-end.js +20 -25
  5. package/{.claude → .tas}/commands/ado-create.md +5 -4
  6. package/{.claude → .tas}/commands/ado-delete.md +5 -4
  7. package/{.claude → .tas}/commands/ado-update.md +5 -4
  8. package/{.claude → .tas}/commands/tas-adr.md +3 -3
  9. package/{.claude → .tas}/commands/tas-apitest-plan.md +2 -2
  10. package/{.claude → .tas}/commands/tas-apitest.md +4 -4
  11. package/{.claude → .tas}/commands/tas-bug.md +6 -6
  12. package/{.claude → .tas}/commands/tas-design.md +3 -3
  13. package/{.claude → .tas}/commands/tas-dev.md +11 -14
  14. package/{.claude → .tas}/commands/tas-epic.md +3 -3
  15. package/{.claude → .tas}/commands/tas-feature.md +4 -4
  16. package/{.claude → .tas}/commands/tas-fix.md +5 -5
  17. package/{.claude → .tas}/commands/tas-init.md +1 -1
  18. package/{.claude → .tas}/commands/tas-plan.md +198 -198
  19. package/{.claude → .tas}/commands/tas-prd.md +3 -3
  20. package/{.claude → .tas}/commands/tas-review.md +17 -15
  21. package/{.claude → .tas}/commands/tas-sad.md +3 -3
  22. package/{.claude → .tas}/commands/tas-security.md +4 -4
  23. package/{.claude → .tas}/commands/tas-story.md +3 -3
  24. package/.tas/platforms.json +5 -0
  25. package/.tas/project-status-example.yaml +17 -17
  26. package/{.claude/skills/ado-integration/SKILL.md → .tas/rules/ado-integration.md} +5 -15
  27. package/{.claude/skills/api-design/SKILL.md → .tas/rules/common/api-design.md} +517 -530
  28. package/{.claude → .tas}/rules/common/code-review.md +30 -6
  29. package/{.claude/rules/common/post-review-agent.md → .tas/rules/common/post-implementation-review.md} +51 -49
  30. package/{.claude → .tas}/rules/common/project-status.md +80 -80
  31. package/{.claude → .tas}/rules/common/stack-detection.md +29 -29
  32. package/.tas/{checklists → rules/common}/story-done.md +12 -5
  33. package/{.claude/skills/tas-tdd/SKILL.md → .tas/rules/common/tdd.md} +4 -38
  34. package/{.claude → .tas}/rules/common/testing.md +3 -8
  35. package/{.claude → .tas}/rules/common/token-logging.md +36 -27
  36. package/{.claude → .tas}/rules/csharp/api-testing.md +171 -171
  37. package/{.claude → .tas}/rules/csharp/coding-style.md +0 -2
  38. package/{.claude → .tas}/rules/csharp/security.md +10 -0
  39. package/{.claude → .tas}/rules/python/coding-style.md +0 -2
  40. package/{.claude → .tas}/rules/typescript/coding-style.md +0 -2
  41. package/.tas/rules/typescript/patterns.md +142 -0
  42. package/.tas/rules/typescript/security.md +88 -0
  43. package/{.claude → .tas}/rules/typescript/testing.md +0 -4
  44. package/{.claude → .tas}/rules/web/coding-style.md +0 -2
  45. package/.tas/tas-example.yaml +125 -126
  46. package/.tas/templates/ADR.md +47 -47
  47. package/.tas/templates/Bug.md +67 -67
  48. package/.tas/templates/Design-Spec.md +36 -36
  49. package/.tas/templates/Epic.md +46 -46
  50. package/.tas/templates/Feature.md +1 -1
  51. package/.tas/templates/Security-Report.md +27 -27
  52. package/.tas/tools/tas-ado-readme.md +169 -169
  53. package/.tas/tools/tas-ado.py +621 -621
  54. package/README.md +334 -334
  55. package/bin/cli.js +91 -73
  56. package/lib/adapters/antigravity.js +131 -0
  57. package/lib/adapters/claude-code.js +35 -0
  58. package/lib/adapters/codex.js +157 -0
  59. package/lib/adapters/cursor.js +80 -0
  60. package/lib/adapters/index.js +20 -0
  61. package/lib/adapters/utils.js +81 -0
  62. package/lib/deleted-files.json +99 -0
  63. package/lib/install.js +543 -327
  64. package/package.json +5 -4
  65. package/.claude/agents/code-reviewer.md +0 -41
  66. package/.claude/agents/e2e-runner.md +0 -61
  67. package/.claude/agents/planner.md +0 -82
  68. package/.claude/agents/tdd-guide.md +0 -84
  69. package/.claude/commands/tas-verify.md +0 -51
  70. package/.claude/rules/typescript/patterns.md +0 -62
  71. package/.claude/rules/typescript/security.md +0 -28
  72. package/.claude/settings.local.json +0 -38
  73. package/.claude/skills/ai-regression-testing/SKILL.md +0 -364
  74. package/.claude/skills/architecture-decision-records/SKILL.md +0 -184
  75. package/.claude/skills/benchmark/SKILL.md +0 -98
  76. package/.claude/skills/browser-qa/SKILL.md +0 -92
  77. package/.claude/skills/canary-watch/SKILL.md +0 -104
  78. package/.claude/skills/js-backend-patterns/SKILL.md +0 -603
  79. package/.claude/skills/tas-conventions/SKILL.md +0 -65
  80. package/.claude/skills/tas-implementation-complete/SKILL.md +0 -100
  81. package/.claude/skills/token-logger/SKILL.md +0 -19
  82. package/.tas/checklists/code-review.md +0 -29
  83. package/.tas/checklists/security.md +0 -21
  84. /package/{.claude → .tas}/agents/architect.md +0 -0
  85. /package/{.claude → .tas}/agents/aws-reviewer.md +0 -0
  86. /package/{.claude → .tas}/agents/build-resolver.md +0 -0
  87. /package/{.claude → .tas}/agents/code-explorer.md +0 -0
  88. /package/{.claude → .tas}/agents/csharp-reviewer.md +0 -0
  89. /package/{.claude → .tas}/agents/database-reviewer.md +0 -0
  90. /package/{.claude → .tas}/agents/doc-updater.md +0 -0
  91. /package/{.claude → .tas}/agents/python-reviewer.md +0 -0
  92. /package/{.claude → .tas}/agents/security-reviewer.md +0 -0
  93. /package/{.claude → .tas}/agents/typescript-reviewer.md +0 -0
  94. /package/{.claude → .tas}/commands/ado-get.md +0 -0
  95. /package/{.claude → .tas}/commands/ado-status.md +0 -0
  96. /package/{.claude → .tas}/commands/tas-brainstorm.md +0 -0
  97. /package/{.claude → .tas}/commands/tas-e2e-mobile.md +0 -0
  98. /package/{.claude → .tas}/commands/tas-e2e-web.md +0 -0
  99. /package/{.claude → .tas}/commands/tas-e2e.md +0 -0
  100. /package/{.claude → .tas}/commands/tas-functest-mobile.md +0 -0
  101. /package/{.claude → .tas}/commands/tas-functest-web.md +0 -0
  102. /package/{.claude → .tas}/commands/tas-functest.md +0 -0
  103. /package/{.claude → .tas}/commands/tas-spec.md +0 -0
  104. /package/{.claude → .tas}/commands/tas-status.md +0 -0
  105. /package/{.claude → .tas}/rules/.gitkeep +0 -0
  106. /package/{.claude → .tas}/rules/common/hooks.md +0 -0
  107. /package/{.claude → .tas}/rules/common/patterns.md +0 -0
  108. /package/{.claude → .tas}/rules/common/security.md +0 -0
  109. /package/{.claude → .tas}/rules/csharp/hooks.md +0 -0
  110. /package/{.claude → .tas}/rules/csharp/patterns.md +0 -0
  111. /package/{.claude → .tas}/rules/csharp/testing.md +0 -0
  112. /package/{.claude → .tas}/rules/python/hooks.md +0 -0
  113. /package/{.claude → .tas}/rules/python/patterns.md +0 -0
  114. /package/{.claude → .tas}/rules/python/security.md +0 -0
  115. /package/{.claude → .tas}/rules/python/testing.md +0 -0
  116. /package/{.claude → .tas}/rules/typescript/hooks.md +0 -0
  117. /package/{.claude → .tas}/rules/web/design-quality.md +0 -0
  118. /package/{.claude → .tas}/rules/web/hooks.md +0 -0
  119. /package/{.claude → .tas}/rules/web/patterns.md +0 -0
  120. /package/{.claude → .tas}/rules/web/performance.md +0 -0
  121. /package/{.claude → .tas}/rules/web/security.md +0 -0
  122. /package/{.claude → .tas}/rules/web/testing.md +0 -0
  123. /package/{CLAUDE-Example.md → .tas/templates/AGENTS.md} +0 -0
@@ -1,171 +1,171 @@
1
- ---
2
- paths:
3
- - "**/*.cs"
4
- - "**/ApiTests/**"
5
- - "**/Api.Tests/**"
6
- ---
7
-
8
- # C# API Automation Testing
9
-
10
- > Used by `/tas-api-test`. Extends [csharp/testing.md](./testing.md).
11
-
12
- ## Tech Stack
13
-
14
- | Component | Choice |
15
- |---|---|
16
- | Framework | xUnit (default) — match project if already using MSTest/NUnit |
17
- | Assertions | FluentAssertions |
18
- | HTTP | System.Net.Http.HttpClient |
19
- | Config | Microsoft.Extensions.Configuration + JSON/EnvVars |
20
-
21
- ```xml
22
- <!-- ApiTests.csproj packages -->
23
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
24
- <PackageReference Include="xunit" Version="2.*" />
25
- <PackageReference Include="xunit.runner.visualstudio" Version="2.*" />
26
- <PackageReference Include="FluentAssertions" Version="6.*" />
27
- <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.*" />
28
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.*" />
29
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.*" />
30
- ```
31
-
32
- ## Project Structure
33
-
34
- ```
35
- tests/ApiTests/
36
- appsettings.json # base (no real secrets)
37
- appsettings.Test.json # Test env override
38
- appsettings.Staging.json # Staging env override
39
- .gitignore # appsettings.*.local.json
40
- Shared/
41
- ApiTestSettings.cs
42
- TestBase.cs
43
- v1/
44
- UsersApiTests.cs
45
- v2/ # APPEND-ONLY: don't modify v1
46
- UsersApiTests.cs
47
- ```
48
-
49
- ## Config
50
-
51
- ```json
52
- // appsettings.json
53
- {
54
- "ApiTest": {
55
- "BaseUrl": "https://localhost:5001",
56
- "TimeoutSeconds": 30,
57
- "Auth": { "Type": "Bearer", "TokenEndpoint": "/api/auth/token", "Username": "", "Password": "" }
58
- }
59
- }
60
- ```
61
-
62
- ```csharp
63
- // ApiTestSettings.cs
64
- public sealed class ApiTestSettings
65
- {
66
- public required string BaseUrl { get; init; }
67
- public int TimeoutSeconds { get; init; } = 30;
68
- public required AuthSettings Auth { get; init; }
69
- }
70
- public sealed class AuthSettings
71
- {
72
- public string Type { get; init; } = "Bearer";
73
- public string TokenEndpoint { get; init; } = "/api/auth/token";
74
- public string Username { get; init; } = "";
75
- public string Password { get; init; } = "";
76
- }
77
- ```
78
-
79
- ```csharp
80
- // TestBase.cs
81
- public abstract class TestBase : IAsyncLifetime
82
- {
83
- protected HttpClient Client { get; private set; } = null!;
84
- protected ApiTestSettings Settings { get; private set; } = null!;
85
-
86
- public async Task InitializeAsync()
87
- {
88
- var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Test";
89
- Settings = new ConfigurationBuilder()
90
- .AddJsonFile("appsettings.json")
91
- .AddJsonFile($"appsettings.{env}.json", optional: true)
92
- .AddEnvironmentVariables("APITEST_")
93
- .Build()
94
- .GetRequiredSection("ApiTest").Get<ApiTestSettings>()!;
95
-
96
- Client = new HttpClient { BaseAddress = new Uri(Settings.BaseUrl),
97
- Timeout = TimeSpan.FromSeconds(Settings.TimeoutSeconds) };
98
-
99
- if (!string.IsNullOrEmpty(Settings.Auth.Username))
100
- await AuthenticateAsync();
101
- }
102
-
103
- public Task DisposeAsync() { Client.Dispose(); return Task.CompletedTask; }
104
-
105
- protected async Task AuthenticateAsync(string? username = null, string? password = null)
106
- {
107
- var res = await Client.PostAsJsonAsync(Settings.Auth.TokenEndpoint,
108
- new { username = username ?? Settings.Auth.Username, password = password ?? Settings.Auth.Password });
109
- res.EnsureSuccessStatusCode();
110
- var token = (await res.Content.ReadFromJsonAsync<TokenResponse>())!.AccessToken;
111
- Client.DefaultRequestHeaders.Authorization =
112
- new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
113
- }
114
-
115
- private sealed record TokenResponse(string AccessToken);
116
- }
117
- ```
118
-
119
- ## Test Class Header (required)
120
-
121
- ```csharp
122
- // ============================================================
123
- // {Resource} API Tests — v{N}
124
- // Spec: {spec-file} | Generated: {YYYY-MM-DD} | Story: {ID}
125
- // APPEND-ONLY: don't modify existing methods.
126
- // ============================================================
127
- namespace ApiTests.V{N};
128
- public sealed class {Resource}ApiTests : TestBase
129
- {
130
- // Inline DTOs — don't import from production code
131
- private sealed record {Resource}Dto(Guid Id, string Name);
132
- private sealed record ListResponse<T>(IReadOnlyList<T> Data, int Total);
133
- }
134
- ```
135
-
136
- ## Test Naming
137
-
138
- ```
139
- {HttpMethod}_{Resource}_Returns{Status}_When{Condition}
140
- ```
141
-
142
- Example: `GetById_User_Returns200_WhenExists`, `Create_Order_Returns422_WhenEmailInvalid`
143
-
144
- AC test: comment `// AC: {text}` right below XML doc.
145
-
146
- ## XML Doc (required on each test)
147
-
148
- ```csharp
149
- /// <summary>Verify {METHOD} {path} → {status} when {condition}. Spec: {ref}</summary>
150
- [Fact]
151
- public async Task ...
152
- ```
153
-
154
- ## Coverage Matrix
155
-
156
- | Condition | Status | When |
157
- |---|---|---|
158
- | Valid, authenticated | 2xx | Always |
159
- | No token | 401 | Endpoint requires auth |
160
- | Insufficient permission | 403 | RBAC / ownership |
161
- | `{id}` doesn't exist | 404 | Has path param |
162
- | Required field missing/invalid | 400/422 | Has request body |
163
- | Business rule (from AC) | 4xx | Story has corresponding AC |
164
-
165
- ## CI/CD Env Vars
166
-
167
- ```
168
- ASPNETCORE_ENVIRONMENT=Test
169
- APITEST__AUTH__USERNAME=... # double __ for nested key
170
- APITEST__AUTH__PASSWORD=...
171
- ```
1
+ ---
2
+ paths:
3
+ - "**/*.cs"
4
+ - "**/ApiTests/**"
5
+ - "**/Api.Tests/**"
6
+ ---
7
+
8
+ # C# API Automation Testing
9
+
10
+ > Used by `/tas-apitest`. Extends [csharp/testing.md](./testing.md).
11
+
12
+ ## Tech Stack
13
+
14
+ | Component | Choice |
15
+ |---|---|
16
+ | Framework | xUnit (default) — match project if already using MSTest/NUnit |
17
+ | Assertions | FluentAssertions |
18
+ | HTTP | System.Net.Http.HttpClient |
19
+ | Config | Microsoft.Extensions.Configuration + JSON/EnvVars |
20
+
21
+ ```xml
22
+ <!-- ApiTests.csproj packages -->
23
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
24
+ <PackageReference Include="xunit" Version="2.*" />
25
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.*" />
26
+ <PackageReference Include="FluentAssertions" Version="6.*" />
27
+ <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.*" />
28
+ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.*" />
29
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.*" />
30
+ ```
31
+
32
+ ## Project Structure
33
+
34
+ ```
35
+ tests/ApiTests/
36
+ appsettings.json # base (no real secrets)
37
+ appsettings.Test.json # Test env override
38
+ appsettings.Staging.json # Staging env override
39
+ .gitignore # appsettings.*.local.json
40
+ Shared/
41
+ ApiTestSettings.cs
42
+ TestBase.cs
43
+ v1/
44
+ UsersApiTests.cs
45
+ v2/ # APPEND-ONLY: don't modify v1
46
+ UsersApiTests.cs
47
+ ```
48
+
49
+ ## Config
50
+
51
+ ```json
52
+ // appsettings.json
53
+ {
54
+ "ApiTest": {
55
+ "BaseUrl": "https://localhost:5001",
56
+ "TimeoutSeconds": 30,
57
+ "Auth": { "Type": "Bearer", "TokenEndpoint": "/api/auth/token", "Username": "", "Password": "" }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ```csharp
63
+ // ApiTestSettings.cs
64
+ public sealed class ApiTestSettings
65
+ {
66
+ public required string BaseUrl { get; init; }
67
+ public int TimeoutSeconds { get; init; } = 30;
68
+ public required AuthSettings Auth { get; init; }
69
+ }
70
+ public sealed class AuthSettings
71
+ {
72
+ public string Type { get; init; } = "Bearer";
73
+ public string TokenEndpoint { get; init; } = "/api/auth/token";
74
+ public string Username { get; init; } = "";
75
+ public string Password { get; init; } = "";
76
+ }
77
+ ```
78
+
79
+ ```csharp
80
+ // TestBase.cs
81
+ public abstract class TestBase : IAsyncLifetime
82
+ {
83
+ protected HttpClient Client { get; private set; } = null!;
84
+ protected ApiTestSettings Settings { get; private set; } = null!;
85
+
86
+ public async Task InitializeAsync()
87
+ {
88
+ var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Test";
89
+ Settings = new ConfigurationBuilder()
90
+ .AddJsonFile("appsettings.json")
91
+ .AddJsonFile($"appsettings.{env}.json", optional: true)
92
+ .AddEnvironmentVariables("APITEST_")
93
+ .Build()
94
+ .GetRequiredSection("ApiTest").Get<ApiTestSettings>()!;
95
+
96
+ Client = new HttpClient { BaseAddress = new Uri(Settings.BaseUrl),
97
+ Timeout = TimeSpan.FromSeconds(Settings.TimeoutSeconds) };
98
+
99
+ if (!string.IsNullOrEmpty(Settings.Auth.Username))
100
+ await AuthenticateAsync();
101
+ }
102
+
103
+ public Task DisposeAsync() { Client.Dispose(); return Task.CompletedTask; }
104
+
105
+ protected async Task AuthenticateAsync(string? username = null, string? password = null)
106
+ {
107
+ var res = await Client.PostAsJsonAsync(Settings.Auth.TokenEndpoint,
108
+ new { username = username ?? Settings.Auth.Username, password = password ?? Settings.Auth.Password });
109
+ res.EnsureSuccessStatusCode();
110
+ var token = (await res.Content.ReadFromJsonAsync<TokenResponse>())!.AccessToken;
111
+ Client.DefaultRequestHeaders.Authorization =
112
+ new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
113
+ }
114
+
115
+ private sealed record TokenResponse(string AccessToken);
116
+ }
117
+ ```
118
+
119
+ ## Test Class Header (required)
120
+
121
+ ```csharp
122
+ // ============================================================
123
+ // {Resource} API Tests — v{N}
124
+ // Spec: {spec-file} | Generated: {YYYY-MM-DD} | Story: {ID}
125
+ // APPEND-ONLY: don't modify existing methods.
126
+ // ============================================================
127
+ namespace ApiTests.V{N};
128
+ public sealed class {Resource}ApiTests : TestBase
129
+ {
130
+ // Inline DTOs — don't import from production code
131
+ private sealed record {Resource}Dto(Guid Id, string Name);
132
+ private sealed record ListResponse<T>(IReadOnlyList<T> Data, int Total);
133
+ }
134
+ ```
135
+
136
+ ## Test Naming
137
+
138
+ ```
139
+ {HttpMethod}_{Resource}_Returns{Status}_When{Condition}
140
+ ```
141
+
142
+ Example: `GetById_User_Returns200_WhenExists`, `Create_Order_Returns422_WhenEmailInvalid`
143
+
144
+ AC test: comment `// AC: {text}` right below XML doc.
145
+
146
+ ## XML Doc (required on each test)
147
+
148
+ ```csharp
149
+ /// <summary>Verify {METHOD} {path} → {status} when {condition}. Spec: {ref}</summary>
150
+ [Fact]
151
+ public async Task ...
152
+ ```
153
+
154
+ ## Coverage Matrix
155
+
156
+ | Condition | Status | When |
157
+ |---|---|---|
158
+ | Valid, authenticated | 2xx | Always |
159
+ | No token | 401 | Endpoint requires auth |
160
+ | Insufficient permission | 403 | RBAC / ownership |
161
+ | `{id}` doesn't exist | 404 | Has path param |
162
+ | Required field missing/invalid | 400/422 | Has request body |
163
+ | Business rule (from AC) | 4xx | Story has corresponding AC |
164
+
165
+ ## CI/CD Env Vars
166
+
167
+ ```
168
+ ASPNETCORE_ENVIRONMENT=Test
169
+ APITEST__AUTH__USERNAME=... # double __ for nested key
170
+ APITEST__AUTH__PASSWORD=...
171
+ ```
@@ -5,8 +5,6 @@ paths:
5
5
  ---
6
6
  # C# Coding Style
7
7
 
8
- > This file extends [common/coding-style.md](../common/coding-style.md) with C#-specific content.
9
-
10
8
  ## Standards
11
9
 
12
10
  - Follow current .NET conventions and enable nullable reference types
@@ -53,6 +53,16 @@ await connection.QueryAsync<Order>(sql, new { customerId });
53
53
  - Log detailed exceptions with structured context server-side
54
54
  - Do not expose stack traces, SQL text, or filesystem paths in API responses
55
55
 
56
+ ## Web / API Hardening
57
+
58
+ - Enforce HTTPS in production (`app.UseHttpsRedirection()`)
59
+ - Enable HSTS (`app.UseHsts()`)
60
+ - Add security headers: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Content-Security-Policy`
61
+ - CORS policy must be restrictive — list allowed origins explicitly, never `AllowAnyOrigin()` in production
62
+ - Anti-forgery token required for state-changing form posts (`[ValidateAntiForgeryToken]`)
63
+ - File upload validation: check size limit, allowed MIME types, sanitize file name, scan for malware before persisting
64
+ - Encrypt PII at rest (column-level encryption, Always Encrypted, or transparent data encryption)
65
+
56
66
  ## References
57
67
 
58
68
  See skill: `security-review` for broader application security review checklists.
@@ -5,8 +5,6 @@ paths:
5
5
  ---
6
6
  # Python Coding Style
7
7
 
8
- > This file extends [common/coding-style.md](../common/coding-style.md) with Python specific content.
9
-
10
8
  ## Standards
11
9
 
12
10
  - Follow **PEP 8** conventions
@@ -7,8 +7,6 @@ paths:
7
7
  ---
8
8
  # TypeScript/JavaScript Coding Style
9
9
 
10
- > This file extends [common/coding-style.md](../common/coding-style.md) with TypeScript/JavaScript specific content.
11
-
12
10
  ## Types and Interfaces
13
11
 
14
12
  Use types to make public APIs, shared models, and component props explicit, readable, and reusable.
@@ -0,0 +1,142 @@
1
+ ---
2
+ paths:
3
+ - "**/*.ts"
4
+ - "**/*.tsx"
5
+ - "**/*.js"
6
+ - "**/*.jsx"
7
+ ---
8
+ # TypeScript/JavaScript Patterns
9
+
10
+ > This file extends [common/patterns.md](../common/patterns.md) with TypeScript/JavaScript specific content.
11
+
12
+ ## API Response Format
13
+
14
+ ```typescript
15
+ interface ApiResponse<T> {
16
+ success: boolean
17
+ data?: T
18
+ error?: string
19
+ meta?: {
20
+ total: number
21
+ page: number
22
+ limit: number
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Custom Hooks Pattern
28
+
29
+ ```typescript
30
+ export function useDebounce<T>(value: T, delay: number): T {
31
+ const [debouncedValue, setDebouncedValue] = useState<T>(value)
32
+
33
+ useEffect(() => {
34
+ const handler = setTimeout(() => setDebouncedValue(value), delay)
35
+ return () => clearTimeout(handler)
36
+ }, [value, delay])
37
+
38
+ return debouncedValue
39
+ }
40
+ ```
41
+
42
+ ## Repository Pattern
43
+
44
+ ```typescript
45
+ interface Repository<T> {
46
+ findAll(filters?: Filters): Promise<T[]>
47
+ findById(id: string): Promise<T | null>
48
+ create(data: CreateDto): Promise<T>
49
+ update(id: string, data: UpdateDto): Promise<T>
50
+ delete(id: string): Promise<void>
51
+ }
52
+ ```
53
+
54
+ ## Performance Anti-Patterns
55
+
56
+ Avoid these in Node.js backends:
57
+
58
+ - **Sequential awaits that could be parallel**: use `Promise.all()` when calls are independent
59
+ - **CPU-blocking on event loop**: offload heavy computation to worker threads
60
+ - **Missing connection pooling**: never create a new DB connection per request — use pooled clients
61
+ - **ORM N+1**: missing `include`/`select` in Prisma/Sequelize — use eager loading or DataLoader for GraphQL
62
+ - **Large file reads without streaming**: avoid `fs.readFile` on large files — use streams instead
63
+
64
+ ## N+1 Query Prevention (Batch Fetch)
65
+
66
+ ```typescript
67
+ // BAD: N+1 queries
68
+ const markets = await getMarkets()
69
+ for (const market of markets) {
70
+ market.creator = await getUser(market.creator_id) // N queries
71
+ }
72
+
73
+ // GOOD: Batch fetch — 2 queries total
74
+ const markets = await getMarkets()
75
+ const creatorIds = markets.map(m => m.creator_id)
76
+ const creators = await getUsers(creatorIds)
77
+ const creatorMap = new Map(creators.map(c => [c.id, c]))
78
+ markets.forEach(m => { m.creator = creatorMap.get(m.creator_id) })
79
+ ```
80
+
81
+ ## Centralized Error Handler
82
+
83
+ Custom `ApiError` class + single error mapper for API routes.
84
+
85
+ ```typescript
86
+ class ApiError extends Error {
87
+ constructor(public statusCode: number, message: string, public isOperational = true) {
88
+ super(message)
89
+ Object.setPrototypeOf(this, ApiError.prototype)
90
+ }
91
+ }
92
+
93
+ export function errorHandler(error: unknown): Response {
94
+ if (error instanceof ApiError) {
95
+ return NextResponse.json({ error: error.message }, { status: error.statusCode })
96
+ }
97
+ if (error instanceof z.ZodError) {
98
+ return NextResponse.json({ error: 'Validation failed', details: error.errors }, { status: 400 })
99
+ }
100
+ console.error('Unexpected error:', error)
101
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
102
+ }
103
+ ```
104
+
105
+ ## Cache-Aside Pattern
106
+
107
+ ```typescript
108
+ async function getMarketWithCache(id: string): Promise<Market> {
109
+ const cacheKey = `market:${id}`
110
+ const cached = await redis.get(cacheKey)
111
+ if (cached) return JSON.parse(cached)
112
+
113
+ const market = await db.markets.findUnique({ where: { id } })
114
+ if (!market) throw new ApiError(404, 'Market not found')
115
+
116
+ await redis.setex(cacheKey, 300, JSON.stringify(market)) // 5 min TTL
117
+ return market
118
+ }
119
+ ```
120
+
121
+ Invalidate on writes: `await redis.del(\`market:\${id}\`)` after update/delete.
122
+
123
+ ## Retry with Exponential Backoff
124
+
125
+ ```typescript
126
+ async function fetchWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
127
+ let lastError: Error
128
+ for (let i = 0; i < maxRetries; i++) {
129
+ try {
130
+ return await fn()
131
+ } catch (error) {
132
+ lastError = error as Error
133
+ if (i < maxRetries - 1) {
134
+ await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000)) // 1s, 2s, 4s
135
+ }
136
+ }
137
+ }
138
+ throw lastError!
139
+ }
140
+ ```
141
+
142
+ Use for transient failures (network, upstream API). Do NOT retry on 4xx client errors.
@@ -0,0 +1,88 @@
1
+ ---
2
+ paths:
3
+ - "**/*.ts"
4
+ - "**/*.tsx"
5
+ - "**/*.js"
6
+ - "**/*.jsx"
7
+ ---
8
+ # TypeScript/JavaScript Security
9
+
10
+ > This file extends [common/security.md](../common/security.md) with TypeScript/JavaScript specific content.
11
+
12
+ ## Secret Management
13
+
14
+ ```typescript
15
+ // NEVER: Hardcoded secrets
16
+ const apiKey = "sk-proj-xxxxx"
17
+
18
+ // ALWAYS: Environment variables
19
+ const apiKey = process.env.OPENAI_API_KEY
20
+
21
+ if (!apiKey) {
22
+ throw new Error('OPENAI_API_KEY not configured')
23
+ }
24
+ ```
25
+
26
+ ## JWT Token Validation
27
+
28
+ ```typescript
29
+ import jwt from 'jsonwebtoken'
30
+
31
+ interface JWTPayload {
32
+ userId: string
33
+ email: string
34
+ role: 'admin' | 'user'
35
+ }
36
+
37
+ export function verifyToken(token: string): JWTPayload {
38
+ try {
39
+ return jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload
40
+ } catch {
41
+ throw new ApiError(401, 'Invalid token')
42
+ }
43
+ }
44
+
45
+ export async function requireAuth(request: Request): Promise<JWTPayload> {
46
+ const token = request.headers.get('authorization')?.replace('Bearer ', '')
47
+ if (!token) throw new ApiError(401, 'Missing authorization token')
48
+ return verifyToken(token)
49
+ }
50
+ ```
51
+
52
+ Always verify `JWT_SECRET` is set at boot. Never log tokens. Use short TTL (15min) + refresh tokens.
53
+
54
+ ## Role-Based Access Control (RBAC)
55
+
56
+ ```typescript
57
+ type Permission = 'read' | 'write' | 'delete' | 'admin'
58
+
59
+ const rolePermissions: Record<User['role'], Permission[]> = {
60
+ admin: ['read', 'write', 'delete', 'admin'],
61
+ moderator: ['read', 'write', 'delete'],
62
+ user: ['read', 'write'],
63
+ }
64
+
65
+ export function hasPermission(user: User, permission: Permission): boolean {
66
+ return rolePermissions[user.role].includes(permission)
67
+ }
68
+
69
+ export function requirePermission(permission: Permission) {
70
+ return (handler: (req: Request, user: User) => Promise<Response>) =>
71
+ async (req: Request) => {
72
+ const user = await requireAuth(req)
73
+ if (!hasPermission(user, permission)) throw new ApiError(403, 'Insufficient permissions')
74
+ return handler(req, user)
75
+ }
76
+ }
77
+
78
+ // Usage
79
+ export const DELETE = requirePermission('delete')(async (req, user) => {
80
+ // handler with verified permission
81
+ })
82
+ ```
83
+
84
+ Check authorization at the route level (not inside business logic). Resource-level ownership checks (e.g., `if (resource.userId !== user.id)`) belong in service layer.
85
+
86
+ ## Agent Support
87
+
88
+ - Use **security-reviewer** skill for comprehensive security audits
@@ -12,7 +12,3 @@ paths:
12
12
  ## E2E Testing
13
13
 
14
14
  Use **Playwright** as the E2E testing framework for critical user flows.
15
-
16
- ## Agent Support
17
-
18
- - **e2e-runner** - Playwright E2E testing specialist
@@ -1,5 +1,3 @@
1
- > This file extends [common/coding-style.md](../common/coding-style.md) with web-specific frontend content.
2
-
3
1
  # Web Coding Style
4
2
 
5
3
  ## File Organization