@gspothq/cli 0.1.1 → 0.2.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 (205) hide show
  1. package/README.md +58 -11
  2. package/configurations/database/postgres/manifest.toml +51 -57
  3. package/configurations/database/postgres/rules/POSTGRES.md +14 -13
  4. package/configurations/framework/astro/manifest.toml +33 -35
  5. package/configurations/framework/astro/rules/ASTRO.md +2 -2
  6. package/configurations/framework/expo/manifest.toml +19 -19
  7. package/configurations/framework/express/manifest.toml +10 -4
  8. package/configurations/framework/express/rules/EXPRESS.md +9 -7
  9. package/configurations/framework/fastapi/manifest.toml +9 -3
  10. package/configurations/framework/fastapi/rules/FASTAPI.md +19 -18
  11. package/configurations/framework/nestjs/manifest.toml +12 -12
  12. package/configurations/framework/nestjs/rules/NESTJS.md +9 -9
  13. package/configurations/framework/nextjs/manifest.toml +39 -79
  14. package/configurations/framework/nextjs/rules/NEXTJS.md +9 -7
  15. package/configurations/framework/nextjs/tool-files/semgrep/nextjs.yml +35 -0
  16. package/configurations/framework/react/manifest.toml +33 -43
  17. package/configurations/framework/react/rules/REACT.md +14 -20
  18. package/configurations/framework/react-dom/manifest.toml +6 -6
  19. package/configurations/framework/react-native/manifest.toml +11 -11
  20. package/configurations/framework/react-native/rules/REACT-NATIVE.md +3 -5
  21. package/configurations/framework/site/manifest.toml +57 -110
  22. package/configurations/framework/site/rules/SITE.md +1 -1
  23. package/configurations/framework/svelte/manifest.toml +29 -31
  24. package/configurations/framework/svelte/rules/SVELTE.md +6 -6
  25. package/configurations/framework/vue/manifest.toml +37 -37
  26. package/configurations/framework/vue/rules/VUE.md +6 -6
  27. package/configurations/general/commits/manifest.toml +20 -84
  28. package/configurations/general/commits/rules/GIT.md +24 -13
  29. package/configurations/general/coverage/manifest.toml +1 -1
  30. package/configurations/general/dependencies/manifest.toml +47 -107
  31. package/configurations/general/dependencies/rules/DEPENDENCIES.md +13 -7
  32. package/configurations/general/docs/manifest.toml +28 -43
  33. package/configurations/general/docs/rules/DOCS.md +16 -9
  34. package/configurations/general/duplication/manifest.toml +12 -14
  35. package/configurations/general/engineering/manifest.toml +6 -2
  36. package/configurations/general/engineering/rules/agent/PLANNING.md +9 -6
  37. package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +20 -11
  38. package/configurations/general/engineering/rules/agent/TASKS.md +6 -2
  39. package/configurations/general/engineering/rules/agent/WORKING.md +34 -29
  40. package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +9 -7
  41. package/configurations/general/engineering/rules/code/CLI.md +12 -9
  42. package/configurations/general/engineering/rules/code/COMMENTS.md +11 -1
  43. package/configurations/general/engineering/rules/code/CONFIGURATION.md +2 -0
  44. package/configurations/general/engineering/rules/code/ERRORS.md +5 -3
  45. package/configurations/general/engineering/rules/code/HTTP.md +9 -10
  46. package/configurations/general/engineering/rules/code/LOGGING.md +10 -9
  47. package/configurations/general/engineering/rules/code/TESTING.md +18 -11
  48. package/configurations/general/files/manifest.toml +45 -73
  49. package/configurations/general/files/rules/YAML.md +20 -10
  50. package/configurations/general/format/manifest.toml +53 -106
  51. package/configurations/general/gspot/manifest.toml +19 -39
  52. package/configurations/general/gspot/rules/GENERATED.md +14 -4
  53. package/configurations/general/licenses/manifest.toml +12 -14
  54. package/configurations/general/naming/defaults.json +14 -13
  55. package/configurations/general/naming/manifest.toml +17 -17
  56. package/configurations/general/naming/rules/NAMING.md +87 -67
  57. package/configurations/general/prose/manifest.toml +166 -25
  58. package/configurations/general/prose/rules/WRITING.md +6 -2
  59. package/configurations/general/repository/manifest.toml +12 -12
  60. package/configurations/general/secrets/manifest.toml +52 -73
  61. package/configurations/general/secrets/rules/SECRETS.md +3 -1
  62. package/configurations/general/security/manifest.toml +29 -52
  63. package/configurations/general/security/rules/SECURITY.md +8 -6
  64. package/configurations/general/spelling/manifest.toml +15 -25
  65. package/configurations/general/structure/manifest.toml +31 -31
  66. package/configurations/infrastructure/ansible/manifest.toml +15 -15
  67. package/configurations/infrastructure/docker/manifest.toml +55 -67
  68. package/configurations/infrastructure/docker/rules/DOCKER.md +15 -13
  69. package/configurations/infrastructure/github-actions/manifest.toml +21 -41
  70. package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +16 -12
  71. package/configurations/infrastructure/github-actions/tool-files/zizmor.yml +5 -0
  72. package/configurations/infrastructure/nginx/manifest.toml +20 -20
  73. package/configurations/infrastructure/nginx/rules/NGINX.md +5 -4
  74. package/configurations/infrastructure/openapi/manifest.toml +20 -20
  75. package/configurations/infrastructure/openapi/rules/OPENAPI.md +3 -1
  76. package/configurations/infrastructure/xcode/manifest.toml +55 -75
  77. package/configurations/infrastructure/xcode/rules/XCODE.md +5 -3
  78. package/configurations/language/bash/manifest.toml +89 -170
  79. package/configurations/language/bash/rules/BASH.md +79 -17
  80. package/configurations/language/bash/rules/LANGUAGE.md +30 -26
  81. package/configurations/language/bash/rules/OPERATIONS.md +10 -8
  82. package/configurations/language/bash/rules/SAFETY.md +39 -35
  83. package/configurations/language/css/manifest.toml +22 -61
  84. package/configurations/language/css/rules/CSS.md +21 -2
  85. package/configurations/language/css/rules/TAILWIND.md +7 -5
  86. package/configurations/language/html/manifest.toml +16 -35
  87. package/configurations/language/html/rules/HTML.md +23 -5
  88. package/configurations/language/javascript/eslint-runtime-names.json +66 -61
  89. package/configurations/language/javascript/eslint-runtime.mjs +118 -0
  90. package/configurations/language/javascript/manifest.toml +135 -207
  91. package/configurations/language/javascript/rules/BUN.md +2 -2
  92. package/configurations/language/javascript/rules/COMPONENTS.md +4 -4
  93. package/configurations/language/javascript/rules/DENO.md +1 -1
  94. package/configurations/language/javascript/rules/JAVASCRIPT.md +30 -17
  95. package/configurations/language/javascript/rules/NODE.md +2 -2
  96. package/configurations/language/javascript/rules/PLAYWRIGHT.md +10 -9
  97. package/configurations/language/markdown/manifest.toml +20 -51
  98. package/configurations/language/python/manifest.toml +231 -342
  99. package/configurations/language/python/rules/DESIGN.md +40 -33
  100. package/configurations/language/python/rules/FLOW.md +8 -6
  101. package/configurations/language/python/rules/PACKAGING.md +14 -7
  102. package/configurations/language/python/rules/PYTHON.md +83 -33
  103. package/configurations/language/python/rules/TYPING.md +25 -23
  104. package/configurations/language/sql/manifest.toml +19 -37
  105. package/configurations/language/sql/rules/SQL.md +90 -2
  106. package/configurations/language/swift/manifest.toml +67 -78
  107. package/configurations/language/swift/rules/SWIFT.md +138 -40
  108. package/configurations/language/swift/rules/SWIFTUI.md +11 -9
  109. package/configurations/language/swift/rules/UIKIT.md +21 -19
  110. package/configurations/language/typescript/manifest.toml +34 -34
  111. package/configurations/language/typescript/rules/TYPESCRIPT.md +62 -10
  112. package/configurations/library/drizzle/manifest.toml +25 -38
  113. package/configurations/library/drizzle/rules/DRIZZLE.md +4 -2
  114. package/configurations/library/react-hook-form/manifest.toml +4 -4
  115. package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +15 -13
  116. package/configurations/library/tanstack-query/manifest.toml +6 -6
  117. package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +5 -3
  118. package/configurations/library/testing-library/manifest.toml +6 -6
  119. package/configurations/library/translations/manifest.toml +22 -22
  120. package/configurations/library/translations/rules/NEXT-INTL.md +6 -8
  121. package/configurations/library/translations/rules/TRANSLATIONS.md +22 -23
  122. package/configurations/library/trpc/manifest.toml +3 -3
  123. package/configurations/library/trpc/rules/TRPC.md +8 -6
  124. package/configurations/library/zod/manifest.toml +6 -6
  125. package/configurations/library/zod/rules/ZOD.md +4 -2
  126. package/configurations/library/zustand/manifest.toml +4 -4
  127. package/configurations/library/zustand/rules/ZUSTAND.md +16 -14
  128. package/configurations/platform/cloudflare/manifest.toml +28 -47
  129. package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +11 -6
  130. package/configurations/platform/supabase/manifest.toml +48 -61
  131. package/configurations/platform/supabase/rules/SUPABASE.md +27 -28
  132. package/configurations/testing/jest/manifest.toml +21 -22
  133. package/configurations/testing/pytest/manifest.toml +28 -28
  134. package/configurations/testing/swift-tests/manifest.toml +18 -29
  135. package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +7 -5
  136. package/configurations/testing/vitest/manifest.toml +32 -32
  137. package/configurations/testing/vitest/rules/VITEST.md +3 -1
  138. package/dist/gspot.js +22297 -24707
  139. package/dist/{linkinator-program.js → linkinator-process.js} +5 -7
  140. package/dist/{purgecss-program.js → purgecss-process.js} +12 -12
  141. package/package.json +7 -9
  142. package/configurations/infrastructure/github-actions/zizmor.yml +0 -3
  143. package/configurations/language/bash/rules/NAMING.md +0 -62
  144. package/configurations/language/css/rules/NAMING.md +0 -17
  145. package/configurations/language/html/rules/NAMING.md +0 -16
  146. package/configurations/language/javascript/rules/NAMING.md +0 -31
  147. package/configurations/language/markdown/markdownlint-cli2.mjs +0 -19
  148. package/configurations/language/python/rules/NAMING.md +0 -53
  149. package/configurations/language/sql/rules/NAMING.md +0 -90
  150. package/configurations/language/swift/rules/NAMING.md +0 -88
  151. package/configurations/language/typescript/rules/NAMING.md +0 -73
  152. package/configurations/library/swift-snapshot-testing/manifest.toml +0 -32
  153. package/dist/THIRD-PARTY-NOTICES.md +0 -25
  154. package/dist/eslint-rule-levels.json +0 -10
  155. package/dist/tool-versions.json +0 -3
  156. package/gspot.schema.json +0 -45991
  157. /package/{dist/LICENSE.md → LICENSE.md} +0 -0
  158. /package/configurations/framework/express/{semgrep → tool-files/semgrep}/express.yml +0 -0
  159. /package/configurations/framework/fastapi/{semgrep → tool-files/semgrep}/fastapi.yml +0 -0
  160. /package/configurations/general/dependencies/{syncpack.json → tool-files/syncpack.json} +0 -0
  161. /package/configurations/general/prose/{vale → tool-files/vale}/styles/LICENSES.md +0 -0
  162. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ampm.yml +0 -0
  163. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/conscious-language.yml +0 -0
  164. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/contractions.yml +0 -0
  165. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/currency.yml +0 -0
  166. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dashes.yml +0 -0
  167. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/date-ordinals.yml +0 -0
  168. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/dates.yml +0 -0
  169. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/file-paths.yml +0 -0
  170. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/future.yml +0 -0
  171. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/hedging.yml +0 -0
  172. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/history.yml +0 -0
  173. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/idioms.yml +0 -0
  174. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/interface-verbs.yml +0 -0
  175. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/jargon.yml +0 -0
  176. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/leading-conjunctions.yml +0 -0
  177. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/link-text.yml +0 -0
  178. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/locations.yml +0 -0
  179. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/marketing.yml +0 -0
  180. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/merge-conflict-markers.yml +0 -0
  181. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/militaristic-language.yml +0 -0
  182. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/modals.yml +0 -0
  183. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/numbered-units.yml +0 -0
  184. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/obvious-fields.yml +0 -0
  185. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/ordinal-adverbs.yml +0 -0
  186. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/percentage-numbers.yml +0 -0
  187. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/placeholders.yml +0 -0
  188. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/product-centric-writing.yml +0 -0
  189. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/punctuated-headings.yml +0 -0
  190. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/self-reference.yml +0 -0
  191. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/session-identifiers.yml +0 -0
  192. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/since.yml +0 -0
  193. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/straight-quotes.yml +0 -0
  194. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/suspended-hyphens.yml +0 -0
  195. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/symbols.yml +0 -0
  196. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/time-ranges.yml +0 -0
  197. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/url-prepositions.yml +0 -0
  198. /package/configurations/general/prose/{vale → tool-files/vale}/styles/gspot/version-range.yml +0 -0
  199. /package/configurations/general/secrets/{gitleaks.toml → tool-files/gitleaks.toml} +0 -0
  200. /package/configurations/infrastructure/docker/{trivyignore → tool-files/trivyignore} +0 -0
  201. /package/configurations/language/bash/{semgrep → tool-files/semgrep}/bash.yml +0 -0
  202. /package/configurations/language/javascript/{semgrep → tool-files/semgrep}/javascript.yml +0 -0
  203. /package/configurations/language/swift/{semgrep → tool-files/semgrep}/swift.yml +0 -0
  204. /package/configurations/platform/cloudflare/{semgrep → tool-files/semgrep}/cloudflare.yml +0 -0
  205. /package/configurations/platform/supabase/{semgrep → tool-files/semgrep}/supabase.yml +0 -0
@@ -1,13 +1,14 @@
1
1
  # FastAPI
2
2
 
3
- Structure, parameters, schemas, responses, errors, and the runtime of a Python project on FastAPI
4
- and Pydantic v2.
3
+ Read this rule when you change a FastAPI application.
4
+
5
+ These rules target FastAPI with Pydantic v2.
5
6
 
6
7
  ## Structure
7
8
 
8
9
  <!-- level: all -->
9
10
 
10
- Path operations adapt HTTP input to application calls and results to HTTP output; business logic
11
+ Path operations turn HTTP input into application calls and results into responses. Business logic
11
12
  lives outside them. One main module creates the `FastAPI` object and wires routers, global
12
13
  dependencies, middleware, exception handlers, and startup. Routers group related operations under
13
14
  a prefix that does not end in `/`, and imports use their domain, `items.router` and `users.router`.
@@ -46,16 +47,16 @@ app.include_router(router)
46
47
 
47
48
  ## Parameters
48
49
 
49
- `Annotated[..., Query(...)]`, `Path`, `Body`, `Header`, `Cookie`, `Form`, `File`, or `Depends`;
50
- a plain scalar supplies a query parameter. A parameter without a default requires a value, even when it
50
+ Declare each parameter with `Annotated[..., Query(...)]`, `Path`, `Body`, `Header`, `Cookie`, `Form`, `File`, or
51
+ `Depends`. A plain scalar supplies a query parameter. A parameter without a default requires a value, even when it
51
52
  accepts `None`. Constraints such as `max_length` or `ge` belong at the boundary when the public interface requires them. Use a dependency for a check that needs a database or authorization state, not
52
53
  a validator. Declare fixed routes such as `/users/me` before `/users/{user_id}`, and group
53
54
  pagination or filter parameters into a query model with `extra="forbid"`.
54
55
 
55
- Forms need `python-multipart`, and one operation never mixes JSON body fields with form fields
56
+ Forms need `python-multipart`, and one operation never mixes JSON body fields with form data
56
57
  or files. Uploads arrive as `UploadFile`, bounded in count and size; `bytes` holds only small
57
- values. Never use a filename as a filesystem path, a content type never proves safety, and never log uploaded
58
- contents.
58
+ values. Never use a filename as a filesystem path or log uploaded contents. A content type proves nothing
59
+ about safety.
59
60
 
60
61
  ## Schemas and responses
61
62
 
@@ -63,32 +64,32 @@ Pydantic models define the schemas where data enters or leaves. Use separate cla
63
64
  their fields differ. Never return an input with passwords or tokens as the response. Known JSON
64
65
  shapes use nested models with typed containers. Use `UUID` for identifiers, timezone-aware instants across a
65
66
  boundary, and `Decimal` for money. A partial update reads
66
- `model_dump(exclude_unset=True)`, merges with the stored value, and validates the complete model,
67
- because `model_copy(update=...)` validates nothing.
67
+ `model_dump(exclude_unset=True)`, merges with the stored value, and validates the whole model;
68
+ `model_copy(update=...)` skips validation.
68
69
 
69
70
  The return type defines the public schema; `response_model` serves a returned object of another
70
71
  shape. A dedicated output model filters fields, never `response_model_exclude`, which still
71
- documents the full model. A response validation failure shows a server bug. Fix the data or
72
+ documents the whole schema. A response validation failure shows a server bug. Fix the data or
72
73
  the schema.
73
74
 
74
75
  ## Status codes and errors
75
76
 
76
77
  Non-default success codes go in the decorator with `fastapi.status` constants: 201 for creation,
77
78
  202 for accepted work, and 204 for a deliberate empty body. Raise `HTTPException` rather than returning it. Its `detail` supplies a public error message. Existence and
78
- authorization checks answer alike, so responses do not reveal a private resource. Global exception
79
- handlers return the standard error shape and never echo a validation error's body.
79
+ authorization checks fail the same way, so responses do not reveal a private resource. Global exception
80
+ handlers return the standard error shape and never echo the request body from a validation failure.
80
81
 
81
82
  ## Runtime
82
83
 
83
- - Declare a path operation or dependency `async def` when it awaits async libraries and `def` when it
84
- calls blocking code; a blocking call inside `async def` stalls the event loop. Run CPU-heavy work in a worker process, not in `async def`.
84
+ - Declare a path operation or dependency `async def` when it awaits async libraries, and `def` when it
85
+ calls blocking code. A blocking call inside `async def` stalls the event loop. Run CPU-heavy work in a worker process, not in `async def`.
85
86
  - Dependencies carry request-scoped concerns: authentication, sessions, pagination, and
86
87
  reusable validation. A `yield` dependency owns one resource, cleans up in `finally`, and
87
88
  re-raises what it catches.
88
89
  - Authentication that appears in OpenAPI uses the security utilities. A JSON Web Token (JWT) verifier allows an
89
90
  explicit algorithm list and requires `exp` and `sub`.
90
- - `BackgroundTasks` runs small follow-up work after the response; durable or heavy work goes to
91
- a queue. Middleware holds cross-cutting HTTP behavior only, and the last middleware added runs
91
+ - `BackgroundTasks` runs small follow-up jobs after the response; durable or heavy work goes to
92
+ a queue. Middleware holds cross-cutting HTTP behavior only, and the one added last runs
92
93
  outermost.
93
94
  - Turn OpenAPI and the interactive docs off unless a typed setting turns them on, and the schema
94
95
  exposes no internal or admin routes.
@@ -97,4 +98,4 @@ handlers return the standard error shape and never echo a validation error's bod
97
98
 
98
99
  Tests cover the HTTP contract through the test client: status codes, bodies, headers, and
99
100
  authentication. Replace external systems with dependency overrides at the application
100
- boundary. Tests cover the application rather than FastAPI's routing implementation.
101
+ boundary. Tests cover the application, not the routing implementation of FastAPI.
@@ -1,11 +1,8 @@
1
- [prefix_collisions]
2
- kinds = ["controller", "service", "module", "guard", "pipe", "filter", "interceptor", "middleware", "decorator", "gateway", "resolver", "repository", "entity", "dto", "strategy", "provider"]
3
-
4
1
  [configuration]
5
- title = "NestJS"
2
+ description = "A NestJS service: the compiler options that decorators need, plugin rules for providers, routes, and DTOs, and no circular modules."
6
3
  requires = ["typescript"]
7
4
  suggests = []
8
- description = "A NestJS service: the compiler options decorators need, the NestJS plugin rules for providers, routes, and DTOs, and no circular modules."
5
+ title = "NestJS"
9
6
 
10
7
  [detect]
11
8
  dependencies = ["@nestjs/core"]
@@ -14,12 +11,15 @@ filenames = ["nest-cli.json"]
14
11
  [files]
15
12
  filenames = ["nest-cli.json"]
16
13
 
14
+ [shared_prefixes]
15
+ kinds = ["controller", "service", "module", "guard", "pipe", "filter", "interceptor", "middleware", "decorator", "gateway", "resolver", "repository", "entity", "dto", "strategy", "provider"]
16
+
17
17
  [[tool]]
18
+ kind = "library"
18
19
  name = "@darraghor/eslint-plugin-nestjs-typed"
20
+ npm = "@darraghor/eslint-plugin-nestjs-typed"
19
21
  rule_prefix = "@darraghor/nestjs-typed"
20
- kind = "library"
21
22
  version = "7.5.5"
22
- npm = "@darraghor/eslint-plugin-nestjs-typed"
23
23
 
24
24
  [eslint]
25
25
  all_level_rules = ["@darraghor/nestjs-typed/api-operation-summary-description-capitalized"]
@@ -33,20 +33,20 @@ package = "@darraghor/eslint-plugin-nestjs-typed"
33
33
  source = "configs.flatNoSwagger"
34
34
 
35
35
  [[eslint.rules_off]]
36
- rules = ["gspot/no-trivial-files", "gspot/no-trivial-functions"]
37
36
  files = ["**/*.module.ts"]
38
37
  reason = "A Nest module is an empty decorated class that registers providers and controllers, which is the shape Nest asks for."
38
+ rules = ["gspot/no-trivial-files", "gspot/no-trivial-functions"]
39
39
 
40
40
  [[eslint.selectors]]
41
- selector = "CallExpression[callee.name='forwardRef']"
42
41
  message = "forwardRef papers over two modules that import each other. Move what both need into a third module."
42
+ selector = "CallExpression[callee.name='forwardRef']"
43
43
 
44
44
  [[eslint.selectors]]
45
- selector = "Decorator[expression.callee.name=/^(Res|Response)$/]:not(:has(Property[key.name='passthrough'][value.value=true]))"
46
45
  message = "Taking the response object turns off interceptors and the return value. Return the value, or pass { passthrough: true }."
46
+ selector = "Decorator[expression.callee.name=/^(Res|Response)$/]:not(:has(Property[key.name='passthrough'][value.value=true]))"
47
47
 
48
48
  [[tool_file]]
49
- target = ".gspot/config/eslint.config.mjs"
50
- tool = "eslint"
51
49
  fragment = true
52
50
  required_tools = ["@darraghor/eslint-plugin-nestjs-typed"]
51
+ target = ".gspot/config/eslint.config.mjs"
52
+ tool = "eslint"
@@ -1,6 +1,6 @@
1
1
  # NestJS
2
2
 
3
- These rules cover NestJS modules, controllers, providers, validation, configuration, and tests.
3
+ Read this rule when you change NestJS modules, controllers, providers, or their tests.
4
4
 
5
5
  ## Request handling
6
6
 
@@ -11,25 +11,25 @@ These rules cover NestJS modules, controllers, providers, validation, configurat
11
11
 
12
12
  ## Providers and lifecycle
13
13
 
14
- - Register each provider with its intended owner. Repeated registration can create distinct instances.
14
+ - Register each provider in the module that owns it. Repeated registration can create distinct instances.
15
15
  - Request-scoped dependencies propagate request scope to their consumers. Check lifetime and
16
- resource costs before selecting that scope.
16
+ resource costs before choosing that scope.
17
17
  - Close connections and timers through the application lifecycle, such as `OnModuleDestroy`.
18
18
 
19
19
  ## Configuration and security
20
20
 
21
21
  - Fail startup when required configuration has missing or malformed values.
22
- - Use guards for route authorization. Enforce additional permissions at each protected operation
23
- when one request performs several operations or the service has non-HTTP callers.
24
- - Guards run before pipes. Do not assume guard inputs have passed DTO transformation or validation.
22
+ - Use guards for route authorization. Enforce extra permissions inside each protected operation
23
+ when one request runs several, or the service has non-HTTP callers.
24
+ - Guards run before pipes. Do not assume guard inputs have passed data transfer object (DTO) transformation or validation.
25
25
  - Set request-size limits, security headers, and rate limits for the exposed application.
26
26
 
27
27
  ## Feature organization
28
28
 
29
29
  <!-- level: all -->
30
30
 
31
- - Give each feature one module and export only providers consumed by other features.
32
- - Inject dependencies through constructors instead of constructing them inside consumers.
31
+ - Give each feature one module, and export only the providers that other modules consume.
32
+ - Inject dependencies through constructors instead of creating them inside consumers.
33
33
  - Define the versioning contract before publishing a public API.
34
34
  - Keep request DTOs distinct from persistence entities and map between them explicitly.
35
35
 
@@ -40,5 +40,5 @@ These rules cover NestJS modules, controllers, providers, validation, configurat
40
40
  - Do not mock the class under test or assert on private methods.
41
41
  - Close test applications in teardown.
42
42
 
43
- See Nest's [response handling](https://docs.nestjs.com/controllers) and
43
+ See the NestJS [response handling](https://docs.nestjs.com/controllers) and
44
44
  [authorization guidance](https://docs.nestjs.com/security/authorization).
@@ -1,25 +1,11 @@
1
- route_files = ["page", "layout", "template", "default", "loading", "error", "not-found", "global-error", "route", "middleware", "proxy"]
2
1
  allowed_words = ["Next.js"]
3
2
  generated = ["next-env.d.ts"]
4
3
 
5
- [[naming.override]]
6
- paths = ["**/app/**", "**/pages/**"]
7
- categories = ["files"]
8
- allowed_names = ["instrumentation", "sitemap", "robots", "manifest", "opengraph-image", "icon", "apple-icon", "_app", "_document", "_error", "404", "500"]
9
- reason = "Next.js names these files."
10
-
11
- [[naming.override]]
12
- paths = ["**"]
13
- languages = ["typescript", "javascript"]
14
- categories = ["functions", "methods", "variables"]
15
- allowed_names = ["getStaticProps", "getServerSideProps", "generateMetadata", "generateStaticParams", "generateViewport"]
16
- reason = "Next.js discovers these data and metadata exports by name."
17
-
18
4
  [configuration]
19
- title = "Next.js"
5
+ description = "A Next.js app: ESLint rules for the framework and hooks, and server code marked server-only. The configuration leaves every build check on."
20
6
  requires = ["typescript", "react", "react-dom"]
21
7
  suggests = ["css"]
22
- description = "A Next.js app: the framework and hooks rules of ESLint, and server code marked as server only. Route segments serve one thing, and the configuration turns no build check off."
8
+ title = "Next.js"
23
9
 
24
10
  [detect]
25
11
  dependencies = ["next"]
@@ -27,75 +13,31 @@ dependencies = ["next"]
27
13
  [files]
28
14
  paths = ["**/next.config.js", "**/next.config.mjs", "**/next.config.ts"]
29
15
 
16
+ [[naming.override]]
17
+ allowed_names = ["page", "layout", "template", "default", "loading", "error", "not-found", "global-error", "route", "middleware", "proxy", "instrumentation", "sitemap", "robots", "manifest", "opengraph-image", "icon", "apple-icon", "_app", "_document", "_error", "404", "500"]
18
+ categories = ["files"]
19
+ paths = ["**/app/**", "**/pages/**"]
20
+ reason = "Next.js names these files."
21
+
22
+ [[naming.override]]
23
+ allowed_names = ["getStaticProps", "getServerSideProps", "generateMetadata", "generateStaticParams", "generateViewport"]
24
+ categories = ["functions", "methods", "variables"]
25
+ languages = ["typescript", "javascript"]
26
+ paths = ["**"]
27
+ reason = "Next.js discovers these data and metadata exports by name."
28
+
30
29
  [[tool]]
30
+ min_version = "15.5.0"
31
31
  name = "next"
32
32
  system = true
33
- min_version = "15.5.0"
34
33
  version_command = ["--version"]
35
34
 
36
35
  [[tool]]
36
+ kind = "library"
37
37
  name = "@next/eslint-plugin-next"
38
+ npm = "@next/eslint-plugin-next"
38
39
  rule_prefix = "@next/next"
39
- kind = "library"
40
40
  version = "16.3.8"
41
- npm = "@next/eslint-plugin-next"
42
-
43
- [[check]]
44
- name = "tsc"
45
- tool = "next"
46
- example = "The TypeScript declaration `const count: number = \"one\";` reports an incompatible assignment. Correct it to `const count: number = 1;` when the contract requires a number."
47
- level = "recommended"
48
- stage = "push"
49
- runs = "scope"
50
- instead_of = "typescript/tsc"
51
- summary = "Has Next.js write its types, then type-checks the app with the strict compiler options."
52
- why = "A fresh clone holds no next-env.d.ts and no route types, so a plain tsc run fails on imports the framework resolves."
53
- help = "Read the first error tsc prints and fix that file; later errors are often the same mistake."
54
-
55
- [[check]]
56
- name = "build"
57
- tool = "next"
58
- example = "A Next.js page importing a nonexistent component fails the project build. Restore the component or correct the import path and rerun the configured build."
59
- level = "all"
60
- stage = "push"
61
- runs = "scope"
62
- needs = ["build"]
63
- summary = "Builds the app with next build at all when its scope contains an app or pages tree."
64
- why = "Some mistakes show only at build time: a server import in a client file, or a page that cannot be prerendered."
65
- help = "Run next build and read the first error it prints."
66
-
67
- [check.files]
68
- paths = ["**/app/**", "**/pages/**"]
69
-
70
- [[check]]
71
- name = "route-segments"
72
- example = "A Next.js segment containing both `page.tsx` and `route.ts` reports conflicting route ownership. Move the route handler into a separate segment, such as `api`, and update its callers."
73
- level = "recommended"
74
- stage = "commit"
75
- runs = "once"
76
- summary = "Checks that no route segment holds both a page and a route handler."
77
- why = "This commit check finds route conflicts before next build and also runs at recommended, where the build check does not."
78
- help = "Move the route handler into a segment of its own, such as an api folder."
79
-
80
- [[check]]
81
- name = "next-config"
82
- example = "Setting `typescript.ignoreBuildErrors` to `true` in Next.js configuration reports a disabled type gate. Remove the switch and correct the type errors before building."
83
- level = "recommended"
84
- stage = "commit"
85
- runs = "once"
86
- summary = "Checks that the framework configuration turns no build check off and puts no secret into the client environment."
87
- why = "A build that ignores lint and type errors ships them, and a key under env is readable by every visitor."
88
- help = "Remove the switch, and read the secret on the server."
89
-
90
- [[check]]
91
- name = "version-pairs"
92
- example = "Different installed versions of `next` and its lint package report an alignment finding. Install the same version of both and update the lockfile with the package manager."
93
- level = "recommended"
94
- stage = "commit"
95
- runs = "once"
96
- summary = "Checks that Next.js and its lint packages use the same installed version."
97
- why = "A lint package a version behind flags what the framework allows and allows what it removed."
98
- help = "Move both packages to the same version."
99
41
 
100
42
  [eslint]
101
43
  all_level_rules = ["@next/next/next-script-for-ga", "@next/next/no-html-link-for-pages", "@next/next/no-img-element", "@next/next/no-page-custom-font"]
@@ -109,11 +51,29 @@ package = "eslint-plugin-react-refresh"
109
51
  source = "configs.next"
110
52
 
111
53
  [[eslint.rules_off]]
112
- rules = ["gspot/no-trivial-files"]
54
+ files = ["**/{page,layout,template,default,loading,error,not-found,global-error,route,middleware,proxy}.{js,jsx,ts,tsx}"]
113
55
  reason = "Next.js discovers route files by name, so each file can hold one export in its own folder."
56
+ rules = ["gspot/no-trivial-files"]
114
57
 
115
58
  [[tool_file]]
116
- target = ".gspot/config/eslint.config.mjs"
117
- tool = "eslint"
118
59
  fragment = true
119
60
  required_tools = ["@next/eslint-plugin-next"]
61
+ target = ".gspot/config/eslint.config.mjs"
62
+ tool = "eslint"
63
+
64
+ [[tool_file]]
65
+ per_scope = true
66
+ target = ".gspot/config/semgrep/nextjs.yml"
67
+ tool = "semgrep"
68
+
69
+ [[check]]
70
+ example = "`const count: number = \"one\";` reports an incompatible assignment. Assign a number, such as `const count: number = 1;`."
71
+ help = "Fix the first error tsc prints; later errors often come from the same mistake."
72
+ instead_of = "typescript/tsc"
73
+ level = "recommended"
74
+ name = "tsc"
75
+ runs = "scope"
76
+ stage = "push"
77
+ summary = "Has Next.js write its types, then type-checks the app with the strict compiler options."
78
+ tool = "next"
79
+ why = "A fresh clone holds no next-env.d.ts and no route types, so a plain tsc run fails on imports the framework resolves."
@@ -1,17 +1,19 @@
1
1
  # Next.js
2
2
 
3
+ Read this rule when you change a Next.js application.
4
+
3
5
  Preserve the project's router, installed Next.js and React versions, runtime, and deployment
4
6
  adapter. App Router guidance applies only to App Router routes.
5
7
 
6
8
  ## Server and client ownership
7
9
 
8
10
  Keep privileged operations and credentials on the server. Send only authorized fields to the
9
- browser. That includes query hydration and Server Component props. A hidden control, type, or filename
10
- does not control access. A client entry includes its imports in the client graph; keep it small
11
+ browser; that covers query hydration and Server Component props too. A hidden control, type, or filename
12
+ does not restrict access. A client boundary pulls its imports into the browser bundle; keep it small
11
13
  and compose server-rendered children into it.
12
14
 
13
- Validate and authorize every mutation at its server entry. Routes, actions, and RPC procedures
14
- share the same domain operation. Initial server reads do not HTTP-fetch the application's own
15
+ Validate and authorize every mutation at its server entry point. Routes, actions, and RPC procedures
16
+ share one domain operation. Initial server reads do not HTTP-fetch the application's own
15
17
  Route Handler. Rendering, `GET` requests, and prefetching perform no writes.
16
18
 
17
19
  See [Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components).
@@ -19,12 +21,12 @@ See [Server and Client Components](https://nextjs.org/docs/app/getting-started/s
19
21
  ## Loading and failures
20
22
 
21
23
  Put a loading boundary around the section that waits. Keep useful content mounted during refresh,
22
- and reset pending state when the selected resource changes. Start independent work early without
24
+ and reset pending state when the user switches resources. Start independent work early without
23
25
  moving protected reads before authorization.
24
26
 
25
27
  Decide status, headers, cookies, redirects, and not-found behavior before streaming commits the
26
28
  response. Preserve framework control-flow exceptions. Check direct load, client navigation,
27
- refresh, Back, and failed prefetch; those paths need not share the same state.
29
+ refresh, Back, and failed prefetch; those paths may differ in state.
28
30
 
29
31
  See [streaming](https://nextjs.org/docs/app/guides/streaming).
30
32
 
@@ -36,7 +38,7 @@ filters, and permissions where they affect results; session tokens stay out of k
36
38
 
37
39
  Default private data to uncached until an isolation design exists. Never let a cached decision
38
40
  retain revoked access. Clear private browser caches on logout. Invalidate after the write commits
39
- and before a terminating redirect; a failed refresh must not retry the committed write.
41
+ and before a terminating redirect; a failed refresh must not retry it.
40
42
  Next.js and a client query cache need separate reconciliation.
41
43
 
42
44
  See [caching](https://nextjs.org/docs/app/getting-started/caching)
@@ -0,0 +1,35 @@
1
+ rules:
2
+ - id: nextjs.checks-off
3
+ languages: [typescript, javascript]
4
+ severity: ERROR
5
+ message: $KEY true lets next build pass with errors. Remove it.
6
+ paths:
7
+ include: ['**/next.config.*']
8
+ patterns:
9
+ - pattern-either:
10
+ - pattern: |
11
+ {..., $KEY: true, ...}
12
+ - pattern: |
13
+ {..., $KEY: (true as $T), ...}
14
+ - pattern: |
15
+ {..., $KEY: true satisfies $T, ...}
16
+ - metavariable-regex:
17
+ metavariable: $KEY
18
+ regex: ^["']?(ignoreBuildErrors|ignoreDuringBuilds)["']?$
19
+ - focus-metavariable: $KEY
20
+ - id: nextjs.env-secret
21
+ languages: [typescript, javascript]
22
+ severity: ERROR
23
+ message: $KEY under env is written into the client bundle. Read it on the server.
24
+ paths:
25
+ include: ['**/next.config.*']
26
+ patterns:
27
+ - pattern-either:
28
+ - pattern: |
29
+ {..., env: {..., $KEY: $V, ...}, ...}
30
+ - pattern: |
31
+ {..., env: {..., $KEY, ...}, ...}
32
+ - metavariable-regex:
33
+ metavariable: $KEY
34
+ regex: ^["']?(?=[A-Z\d_]*(?:SECRET|TOKEN|PASSWORD|PRIVATE_KEY|API_KEY))[A-Z][A-Z\d_]*["']?$
35
+ - focus-metavariable: $KEY
@@ -1,63 +1,56 @@
1
1
  allowed_words = ["React"]
2
2
 
3
3
  [configuration]
4
- title = "React"
4
+ description = "React components: the recommended React and hooks rules as errors, and component files that keep their state on a reload."
5
5
  requires = ["javascript"]
6
6
  suggests = ["typescript", "css", "react-dom"]
7
- description = "React components: the recommended React and hooks rules as errors, component files that keep their state on a reload."
7
+ title = "React"
8
8
 
9
9
  [detect]
10
10
  dependencies = ["react"]
11
11
 
12
+ [defaults]
13
+ "tools.eslint.import_extension" = "extensionless"
14
+
15
+ [[naming.override]]
16
+ cases = ["pascal", "kebab"]
17
+ categories = ["files"]
18
+ paths = ["**/*.tsx", "**/*.jsx"]
19
+ reason = "A component file takes the component's name."
20
+
21
+ [[naming.override]]
22
+ cases = ["pascal", "camel"]
23
+ categories = ["functions"]
24
+ paths = ["**/*.tsx", "**/*.jsx"]
25
+ reason = "A function that returns JSX is a component and takes a capital."
26
+
27
+ [[naming.override]]
28
+ allowed_names = ["useSyncExternalStore", "getSnapshot", "getServerSnapshot"]
29
+ categories = ["functions", "methods", "variables"]
30
+ languages = ["typescript", "javascript"]
31
+ paths = ["**"]
32
+ reason = "React defines these subscription and snapshot callback names."
33
+
12
34
  [[tool]]
35
+ kind = "library"
13
36
  name = "eslint-plugin-react-hooks"
37
+ npm = "eslint-plugin-react-hooks"
14
38
  rule_prefix = "react-hooks"
15
- kind = "library"
16
39
  version = "7.1.1"
17
- npm = "eslint-plugin-react-hooks"
18
40
 
19
41
  [[tool]]
42
+ kind = "library"
20
43
  name = "eslint-plugin-react"
44
+ npm = "eslint-plugin-react"
21
45
  rule_prefix = "react"
22
- kind = "library"
23
46
  version = "7.37.5"
24
- npm = "eslint-plugin-react"
25
47
 
26
48
  [[tool]]
49
+ kind = "library"
27
50
  name = "eslint-plugin-react-refresh"
51
+ npm = "eslint-plugin-react-refresh"
28
52
  rule_prefix = "react-refresh"
29
- kind = "library"
30
53
  version = "0.5.7"
31
- npm = "eslint-plugin-react-refresh"
32
-
33
- [[check]]
34
- name = "version-pairs"
35
- example = "Different installed versions of react and react-dom report an alignment finding. Install the same version of both and update the lockfile with the package manager."
36
- level = "recommended"
37
- stage = "commit"
38
- runs = "once"
39
- summary = "Checks that react and react-dom use the same installed version."
40
- why = "React and its DOM renderer ship together and expect matching versions."
41
- help = "Install the same version of react and react-dom."
42
-
43
- [[naming.override]]
44
- paths = ["**/*.tsx", "**/*.jsx"]
45
- categories = ["files"]
46
- cases = ["pascal", "kebab"]
47
- reason = "A component file takes the component's name."
48
-
49
- [[naming.override]]
50
- paths = ["**/*.tsx", "**/*.jsx"]
51
- categories = ["functions"]
52
- cases = ["pascal", "camel"]
53
- reason = "A function that returns JSX is a component and takes a capital."
54
-
55
- [[naming.override]]
56
- paths = ["**"]
57
- languages = ["typescript", "javascript"]
58
- categories = ["functions", "methods", "variables"]
59
- allowed_names = ["useSyncExternalStore", "getSnapshot", "getServerSnapshot"]
60
- reason = "React defines these subscription and snapshot callback names."
61
54
 
62
55
  [eslint]
63
56
  all_level_rules = ["react/self-closing-comp", "react/display-name", "react/no-array-index-key", "react/prop-types"]
@@ -79,15 +72,12 @@ package = "eslint-plugin-react-refresh"
79
72
  source = "configs.recommended"
80
73
 
81
74
  [[eslint.rules_off]]
82
- rules = ["gspot/no-trivial-files", "gspot/no-trivial-functions"]
83
75
  files = ["**/*.tsx", "**/*.jsx"]
84
76
  reason = "React renders a component by its name, so a component of one statement, alone in its file, is the shape React asks for."
85
-
86
- [defaults]
87
- "tools.eslint.import_extension" = "extensionless"
77
+ rules = ["gspot/no-trivial-files", "gspot/no-trivial-functions"]
88
78
 
89
79
  [[tool_file]]
90
- target = ".gspot/config/eslint.config.mjs"
91
- tool = "eslint"
92
80
  fragment = true
93
81
  required_tools = ["eslint-plugin-react-hooks", "eslint-plugin-react", "eslint-plugin-react-refresh"]
82
+ target = ".gspot/config/eslint.config.mjs"
83
+ tool = "eslint"
@@ -1,7 +1,6 @@
1
1
  # React
2
2
 
3
- Rules that hold for any React application: Next.js, Vite, Expo, or a library. The framework file
4
- adds routing, server components, and caching on top of these.
3
+ Read this rule when you change React components, hooks, or state.
5
4
 
6
5
  ## Components
7
6
 
@@ -15,7 +14,7 @@ adds routing, server components, and caching on top of these.
15
14
 
16
15
  - Compose downward: a component imports children and shared primitives, never its parent, its
17
16
  section, or its route.
18
- - Domain-neutral primitives such as button, input, dialog, popover, and tooltip live in the shared UI owner
17
+ - Domain-neutral primitives such as button, input, dialog, popover, and tooltip live in the shared UI folder
19
18
  and take values and callbacks. They never fetch or import a feature.
20
19
  - Keep a component small enough that its JSX reads as one screen region. Extract by visual
21
20
  responsibility, not by line count.
@@ -26,18 +25,18 @@ adds routing, server components, and caching on top of these.
26
25
  ## Hooks
27
26
 
28
27
  - Every custom hook starts with `use` and owns one concern.
29
- - Memoize such as `useMemo`, `useCallback`, `memo` when a measurement shows expensive work or a
30
- consumer needs a stable reference, not by default.
28
+ - Memoize with `useMemo`, `useCallback`, or `memo` only when a measurement shows expensive work or a consumer
29
+ needs a stable reference.
31
30
 
32
31
  ## Effects and state
33
32
 
34
33
  - Keep renders pure. Effects synchronize external systems; handlers own user-triggered work. Derive
35
34
  ordinary values during render rather than synchronizing duplicate state with effects. Clean up
36
35
  timers, observers, subscriptions, animation frames, and requests.
37
- - Use refs for mutable controller values and state for values displayed in the UI.
36
+ - Use refs for mutable controller values and state for what the UI displays.
38
37
 
39
- - Give each state value one owner: URL state for navigation, query cache for server snapshots, form/local
40
- state for editing, and client store for truly shared client state.
38
+ - Give each state value one place. Navigation goes in the URL, server snapshots in the query cache, edits in
39
+ form or local state, and shared data in a client store.
41
40
 
42
41
  - Reset private state across identity and tenant changes; give each request its own server stores.
43
42
 
@@ -47,23 +46,23 @@ adds routing, server components, and caching on top of these.
47
46
 
48
47
  ## Read states and writes
49
48
 
50
- - Keep the first pending request, an empty result, and a failed request distinct. Show a stable
49
+ - Keep the first pending load, an empty result, and a failed request distinct. Show a stable
51
50
  fallback, an explanation of the empty result, or safe error and retry UI for the matching state.
52
51
  - Keep existing content during a background refresh or a pending next page. A failed refresh does
53
52
  not erase a successful result. Revoked access removes stale private content.
54
53
  - Capture the resource identity when work starts. A superseded request must not overwrite the
55
- currently selected resource.
54
+ resource now in view.
56
55
  - An optimistic update defines how to confirm or undo the operation.
57
- - Present the result of a save separately from the result of a refresh.
56
+ - Show the result of a save apart from that of a refresh.
58
57
 
59
58
  ## URL state
60
59
 
61
- - The URL owns filters, sorting, and other shareable navigation values. It carries a selected,
60
+ - The URL owns filters, sorting, and other shareable navigation values. It carries a minimal,
62
61
  versioned representation, never credentials, private drafts, or the whole store.
63
62
  - Validate parsed values, and handle missing or malformed input without assertions or an unchecked
64
63
  `JSON.parse`.
65
64
  - Define the precedence among URL values, stored preferences, and defaults. When the URL owns a
66
- value, back and forward navigation reach the state owner.
65
+ value, back and forward navigation reach the code that holds it.
67
66
  - One synchronization path compares values before writing, so store-to-URL and URL-to-store updates
68
67
  never loop.
69
68
  - Keep unrelated query parameters and fragments. Use the installed router or the History API to replace history for transient edits and to push an entry when back navigation restores the previous state.
@@ -83,7 +82,7 @@ UI updates after hydration. Fix the mismatch instead of hiding warnings globally
83
82
  - Give date/time formatting the same initial locale, time zone, and reference time where relative output otherwise differs.
84
83
  - Do not branch rendered markup on `typeof window` as a routine hydration fix. Server and first
85
84
  browser output agree on the visible tree.
86
- - Theme initialization may need the theme library's documented early script and a narrow hydration
85
+ - Theme initialization may need the documented early script of its library and a narrow hydration
87
86
  exception. Keep that exception local to the affected node.
88
87
  - Do not mutate React-owned DOM to coordinate application state. Controlled state, refs, and
89
88
  third-party imperative APIs have explicit ownership boundaries.
@@ -98,11 +97,6 @@ UI updates after hydration. Fix the mismatch instead of hiding warnings globally
98
97
  ## Forms and inputs
99
98
 
100
99
  - Keep each input controlled or uncontrolled, never both. Choose per form and keep it.
101
- - Validate input against the declared contract and expose its errors to the user. When the application uses a form
100
+ - Validate input against the declared contract and expose its errors to the user. When the application has a form
102
101
  library, use its supported validation and state APIs.
103
102
  - A form awaits the save and reports its result.
104
-
105
- ## Accessibility
106
-
107
- - Use native interactive elements such as `button`, `a`, and `input`, or give each element the matching role,
108
- keyboard handling, and focus management.