@gspothq/cli 0.0.0-stage → 0.1.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 (232) hide show
  1. package/README.md +18 -2
  2. package/configurations/database/postgres/manifest.toml +114 -0
  3. package/configurations/database/postgres/rules/POSTGRES.md +78 -0
  4. package/configurations/framework/astro/eslint-presets.json +138 -0
  5. package/configurations/framework/astro/manifest.toml +95 -0
  6. package/configurations/framework/astro/rules/ASTRO.md +39 -0
  7. package/configurations/framework/expo/manifest.toml +43 -0
  8. package/configurations/framework/express/manifest.toml +17 -0
  9. package/configurations/framework/express/rules/EXPRESS.md +73 -0
  10. package/configurations/framework/express/semgrep/express.yml +71 -0
  11. package/configurations/framework/fastapi/manifest.toml +11 -0
  12. package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
  13. package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
  14. package/configurations/framework/nestjs/eslint-presets.json +62 -0
  15. package/configurations/framework/nestjs/manifest.toml +52 -0
  16. package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
  17. package/configurations/framework/nextjs/eslint-presets.json +73 -0
  18. package/configurations/framework/nextjs/manifest.toml +119 -0
  19. package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
  20. package/configurations/framework/react/eslint-presets.json +77 -0
  21. package/configurations/framework/react/manifest.toml +93 -0
  22. package/configurations/framework/react/rules/REACT.md +108 -0
  23. package/configurations/framework/react-dom/eslint-presets.json +110 -0
  24. package/configurations/framework/react-dom/manifest.toml +24 -0
  25. package/configurations/framework/react-native/manifest.toml +46 -0
  26. package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
  27. package/configurations/framework/site/manifest.toml +170 -0
  28. package/configurations/framework/site/rules/SITE.md +20 -0
  29. package/configurations/framework/svelte/eslint-presets.json +67 -0
  30. package/configurations/framework/svelte/manifest.toml +83 -0
  31. package/configurations/framework/svelte/rules/SVELTE.md +48 -0
  32. package/configurations/framework/vue/eslint-presets.json +180 -0
  33. package/configurations/framework/vue/manifest.toml +83 -0
  34. package/configurations/framework/vue/rules/VUE.md +41 -0
  35. package/configurations/general/commits/manifest.toml +111 -0
  36. package/configurations/general/commits/rules/GIT.md +31 -0
  37. package/configurations/general/coverage/manifest.toml +3 -0
  38. package/configurations/general/dependencies/manifest.toml +159 -0
  39. package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
  40. package/configurations/general/dependencies/syncpack.json +15 -0
  41. package/configurations/general/docs/manifest.toml +79 -0
  42. package/configurations/general/docs/rules/DOCS.md +86 -0
  43. package/configurations/general/duplication/manifest.toml +32 -0
  44. package/configurations/general/engineering/manifest.toml +8 -0
  45. package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
  46. package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
  47. package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
  48. package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
  49. package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
  50. package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
  51. package/configurations/general/engineering/rules/code/CLI.md +37 -0
  52. package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
  53. package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
  54. package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
  55. package/configurations/general/engineering/rules/code/HTTP.md +46 -0
  56. package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
  57. package/configurations/general/engineering/rules/code/TESTING.md +58 -0
  58. package/configurations/general/files/manifest.toml +141 -0
  59. package/configurations/general/files/rules/YAML.md +19 -0
  60. package/configurations/general/format/manifest.toml +152 -0
  61. package/configurations/general/gspot/manifest.toml +65 -0
  62. package/configurations/general/gspot/rules/GENERATED.md +10 -0
  63. package/configurations/general/licenses/manifest.toml +33 -0
  64. package/configurations/general/naming/defaults.json +423 -0
  65. package/configurations/general/naming/manifest.toml +35 -0
  66. package/configurations/general/naming/rules/NAMING.md +179 -0
  67. package/configurations/general/prose/manifest.toml +86 -0
  68. package/configurations/general/prose/rules/WRITING.md +31 -0
  69. package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
  70. package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
  71. package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
  72. package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
  73. package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
  74. package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
  75. package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
  76. package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
  77. package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
  78. package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
  79. package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
  80. package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
  81. package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
  82. package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
  83. package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
  84. package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
  85. package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
  86. package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
  87. package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
  88. package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
  89. package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
  90. package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
  91. package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
  92. package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
  93. package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
  94. package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
  95. package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
  96. package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
  97. package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
  98. package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
  99. package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
  100. package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
  101. package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
  102. package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
  103. package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
  104. package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
  105. package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
  106. package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
  107. package/configurations/general/repository/manifest.toml +24 -0
  108. package/configurations/general/secrets/gitleaks.toml +2 -0
  109. package/configurations/general/secrets/manifest.toml +125 -0
  110. package/configurations/general/secrets/rules/SECRETS.md +13 -0
  111. package/configurations/general/security/manifest.toml +77 -0
  112. package/configurations/general/security/rules/SECURITY.md +55 -0
  113. package/configurations/general/spelling/manifest.toml +46 -0
  114. package/configurations/general/structure/manifest.toml +70 -0
  115. package/configurations/infrastructure/ansible/manifest.toml +33 -0
  116. package/configurations/infrastructure/docker/manifest.toml +127 -0
  117. package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
  118. package/configurations/infrastructure/docker/trivyignore +2 -0
  119. package/configurations/infrastructure/github-actions/manifest.toml +76 -0
  120. package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
  121. package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
  122. package/configurations/infrastructure/nginx/manifest.toml +51 -0
  123. package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
  124. package/configurations/infrastructure/openapi/manifest.toml +50 -0
  125. package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
  126. package/configurations/infrastructure/xcode/manifest.toml +131 -0
  127. package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
  128. package/configurations/language/bash/manifest.toml +261 -0
  129. package/configurations/language/bash/rules/BASH.md +106 -0
  130. package/configurations/language/bash/rules/LANGUAGE.md +213 -0
  131. package/configurations/language/bash/rules/NAMING.md +62 -0
  132. package/configurations/language/bash/rules/OPERATIONS.md +77 -0
  133. package/configurations/language/bash/rules/SAFETY.md +222 -0
  134. package/configurations/language/bash/semgrep/bash.yml +30 -0
  135. package/configurations/language/css/manifest.toml +98 -0
  136. package/configurations/language/css/rules/CSS.md +28 -0
  137. package/configurations/language/css/rules/NAMING.md +17 -0
  138. package/configurations/language/css/rules/TAILWIND.md +26 -0
  139. package/configurations/language/css/stylelint-rule-names.json +89 -0
  140. package/configurations/language/html/manifest.toml +59 -0
  141. package/configurations/language/html/rules/HTML.md +27 -0
  142. package/configurations/language/html/rules/NAMING.md +16 -0
  143. package/configurations/language/javascript/eslint-presets.json +997 -0
  144. package/configurations/language/javascript/eslint-rule-names.json +299 -0
  145. package/configurations/language/javascript/eslint-runtime-names.json +61 -0
  146. package/configurations/language/javascript/manifest.toml +393 -0
  147. package/configurations/language/javascript/rules/BUN.md +13 -0
  148. package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
  149. package/configurations/language/javascript/rules/DENO.md +15 -0
  150. package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
  151. package/configurations/language/javascript/rules/NAMING.md +31 -0
  152. package/configurations/language/javascript/rules/NODE.md +21 -0
  153. package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
  154. package/configurations/language/javascript/semgrep/javascript.yml +84 -0
  155. package/configurations/language/markdown/manifest.toml +85 -0
  156. package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
  157. package/configurations/language/python/manifest.toml +621 -0
  158. package/configurations/language/python/rules/DESIGN.md +136 -0
  159. package/configurations/language/python/rules/FLOW.md +124 -0
  160. package/configurations/language/python/rules/NAMING.md +53 -0
  161. package/configurations/language/python/rules/PACKAGING.md +49 -0
  162. package/configurations/language/python/rules/PYTHON.md +136 -0
  163. package/configurations/language/python/rules/TYPING.md +132 -0
  164. package/configurations/language/sql/manifest.toml +68 -0
  165. package/configurations/language/sql/rules/NAMING.md +90 -0
  166. package/configurations/language/sql/rules/SQL.md +9 -0
  167. package/configurations/language/swift/manifest.toml +164 -0
  168. package/configurations/language/swift/rules/NAMING.md +88 -0
  169. package/configurations/language/swift/rules/SWIFT.md +134 -0
  170. package/configurations/language/swift/rules/SWIFTUI.md +61 -0
  171. package/configurations/language/swift/rules/UIKIT.md +99 -0
  172. package/configurations/language/swift/semgrep/swift.yml +126 -0
  173. package/configurations/language/typescript/eslint-presets.json +215 -0
  174. package/configurations/language/typescript/manifest.toml +72 -0
  175. package/configurations/language/typescript/rules/NAMING.md +73 -0
  176. package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
  177. package/configurations/library/drizzle/manifest.toml +68 -0
  178. package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
  179. package/configurations/library/react-hook-form/manifest.toml +15 -0
  180. package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
  181. package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
  182. package/configurations/library/tanstack-query/eslint-presets.json +21 -0
  183. package/configurations/library/tanstack-query/manifest.toml +26 -0
  184. package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
  185. package/configurations/library/testing-library/eslint-presets.json +191 -0
  186. package/configurations/library/testing-library/manifest.toml +47 -0
  187. package/configurations/library/translations/manifest.toml +40 -0
  188. package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
  189. package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
  190. package/configurations/library/trpc/manifest.toml +14 -0
  191. package/configurations/library/trpc/rules/TRPC.md +43 -0
  192. package/configurations/library/zod/manifest.toml +25 -0
  193. package/configurations/library/zod/rules/ZOD.md +44 -0
  194. package/configurations/library/zustand/manifest.toml +18 -0
  195. package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
  196. package/configurations/platform/cloudflare/manifest.toml +94 -0
  197. package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
  198. package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
  199. package/configurations/platform/supabase/manifest.toml +112 -0
  200. package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
  201. package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
  202. package/configurations/testing/jest/manifest.toml +54 -0
  203. package/configurations/testing/pytest/manifest.toml +53 -0
  204. package/configurations/testing/swift-tests/manifest.toml +52 -0
  205. package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
  206. package/configurations/testing/vitest/eslint-presets.json +22 -0
  207. package/configurations/testing/vitest/manifest.toml +69 -0
  208. package/configurations/testing/vitest/rules/VITEST.md +18 -0
  209. package/dist/LICENSE.md +201 -0
  210. package/dist/THIRD-PARTY-NOTICES.md +25 -0
  211. package/dist/eslint-rule-levels.json +10 -0
  212. package/dist/gspot.js +30740 -0
  213. package/dist/linkinator-program.js +65 -0
  214. package/dist/purgecss-program.js +51 -0
  215. package/dist/tool-versions.json +3 -0
  216. package/grammars/bash.wasm +0 -0
  217. package/grammars/css.wasm +0 -0
  218. package/grammars/html.wasm +0 -0
  219. package/grammars/javascript.wasm +0 -0
  220. package/grammars/licenses/tree-sitter-bash.txt +21 -0
  221. package/grammars/licenses/tree-sitter-css.txt +21 -0
  222. package/grammars/licenses/tree-sitter-html.txt +21 -0
  223. package/grammars/licenses/tree-sitter-javascript.txt +21 -0
  224. package/grammars/licenses/tree-sitter-python.txt +21 -0
  225. package/grammars/licenses/tree-sitter-swift.txt +21 -0
  226. package/grammars/licenses/tree-sitter-typescript.txt +21 -0
  227. package/grammars/python.wasm +0 -0
  228. package/grammars/swift.wasm +0 -0
  229. package/grammars/tsx.wasm +0 -0
  230. package/grammars/typescript.wasm +0 -0
  231. package/gspot.schema.json +45991 -0
  232. package/package.json +93 -5
package/README.md CHANGED
@@ -1,3 +1,19 @@
1
- # Temporary Holding Version
1
+ # gspot
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ gspot lints AI-generated code and writes rules for AI coding agents.
4
+
5
+ ## Install
6
+
7
+ Commit your work, then run:
8
+
9
+ ```sh
10
+ npm install --save-dev --save-exact @gspothq/cli
11
+ git add package.json package-lock.json
12
+ git commit -m "build: Add gspot"
13
+ npx gspot init
14
+ npx gspot install
15
+ npx gspot check
16
+ ```
17
+
18
+ The [install guide](https://generativespotting.com/guides/install/) covers other package managers and mise. The
19
+ [gspot guides and reference](https://generativespotting.com/) cover everything else.
@@ -0,0 +1,114 @@
1
+ allowed_words = ["PostgreSQL", "Postgres"]
2
+ [configuration]
3
+ title = "Postgres"
4
+ requires = ["sql"]
5
+ description = "Postgres migrations: squawk for the changes that lock or break, ordered versions, and migrations that never change after they run. The schema checks ask for row security, explicit grants, and an index under every foreign key."
6
+
7
+ [detect]
8
+ dependencies = ["pg", "postgres", "psycopg", "asyncpg", "@neondatabase/serverless"]
9
+ content = { "prisma/schema.prisma" = '(?:^|\n)[\t ]*provider[\t ]*=[\t ]*"postgresql"' }
10
+
11
+ [files]
12
+ paths = ["db/migrations/*.sql", "migrations/*.sql", "**/supabase/migrations/*.sql"]
13
+
14
+ [[tool]]
15
+ name = "squawk"
16
+ version = "2.64.0"
17
+ min_version = "2.0.0"
18
+ mise = "github:sbdchd/squawk"
19
+ npm = "squawk-cli"
20
+ github = { name = "sbdchd/squawk", version = "v2.64.0" }
21
+ [[tool.replace]]
22
+ file = ".squawk.toml"
23
+ shared = false
24
+
25
+ [[tool.replace]]
26
+ file = "squawk.toml"
27
+ shared = false
28
+
29
+ [[tool_file]]
30
+ target = ".gspot/config/squawk.toml"
31
+ tool = "squawk"
32
+ per_scope = true
33
+ rule_keys = ["excluded_rules"]
34
+
35
+ [[check]]
36
+ name = "squawk"
37
+ files = { paths = ["{setting:postgres.migrations_folder}/**/*.sql"] }
38
+ example = "A migration adding a constraint that requires a disruptive table scan can report a migration-safety finding. Follow the reported rule to separate creation and validation or otherwise reduce the lock, then review the migration against the deployment database."
39
+ level = "recommended"
40
+ stage = "commit"
41
+ command = ["squawk", "--config", "{tool_file:squawk}", "--reporter", "json", "{files}"]
42
+ output = { format = "json", line_base = 0, fields = { file = "file", line = "line", column = "column", rule = "rule_name", message = "message" } }
43
+ summary = "Reads every migration for the change that locks a table, rewrites it, or breaks the running application."
44
+ why = "A migration that takes an exclusive lock on a large table stops the application for as long as it runs."
45
+ help = "Follow the help squawk prints for the rule. Turn one rule off with gspot ignore postgres/squawk --rule `<name>` --reason \"`<why>`\"."
46
+
47
+ [[check]]
48
+ name = "migration-order"
49
+ example = "A new migration numbered before the latest committed migration reports an ordering finding. Give the new migration a version later than the committed maximum without renaming already applied migrations."
50
+ level = "recommended"
51
+ stage = "commit"
52
+ runs = "scope"
53
+ summary = "Checks that every migration has a version, that no two share one, and that a new migration sorts after the committed ones."
54
+ why = "A migration that sorts before one already applied never runs on the databases that are ahead of it."
55
+ help = "Rename the new migration with a version later than the newest committed one."
56
+
57
+ [[check]]
58
+ name = "edited-migrations"
59
+ example = "Editing an already committed migration reports a frozen-history finding. Preserve the intended change as a new migration, restore the original migration bytes, and rerun. Do not rewrite a migration already applied to shared databases."
60
+ level = "recommended"
61
+ stage = "commit"
62
+ runs = "scope"
63
+ summary = "Checks that no migration at or before postgres.frozen_through differs from its committed text."
64
+ why = "A migration that ran never runs again, so an edit to it changes new databases only and the environments drift apart."
65
+ help = "Restore the file with git checkout, and write the change as a new migration."
66
+
67
+ [[check]]
68
+ name = "row-level-security"
69
+ example = "A newly created table without row-level security reports a missing-RLS finding. Add `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` for the table."
70
+ level = "recommended"
71
+ stage = "commit"
72
+ runs = "scope"
73
+ summary = "Checks that every table in a client schema turns on row level security."
74
+ why = "A table in an exposed schema with no row security is readable and writable by every client that holds the public key."
75
+ help = "Enable row level security on the table."
76
+
77
+ [[check]]
78
+ name = "grants"
79
+ example = "The statement `GRANT ALL ON TABLE public.items TO app_reader;` reports a broad grant. If the role only reads rows, use `GRANT SELECT ON TABLE public.items TO app_reader;`."
80
+ level = "recommended"
81
+ stage = "commit"
82
+ runs = "scope"
83
+ summary = "Refuses a grant of every privilege in a migration."
84
+ why = "Granting everything includes truncating the table, adding triggers, and every privilege a later Postgres adds, which nobody meant to give."
85
+ help = "Name the privileges the role needs, such as `SELECT` alone."
86
+
87
+ [[check]]
88
+ name = "definer-search-path"
89
+ example = "A `SECURITY DEFINER` function without an explicit safe search path reports a finding. Add `SET search_path = ''` to the definition and schema-qualify the objects used in its body."
90
+ level = "recommended"
91
+ stage = "commit"
92
+ runs = "scope"
93
+ summary = "Checks that every SECURITY DEFINER function sets its search_path."
94
+ why = "A definer function runs with its owner's rights, and a caller who controls the search path chooses the objects it touches."
95
+ help = "Give the function an empty `search_path` in its definition, and qualify every name inside it."
96
+
97
+ [[check]]
98
+ name = "foreign-key-indexes"
99
+ example = "A foreign key on `orders.customer_id` without an index starting with that column reports a missing index. Add `CREATE INDEX orders_customer_id_idx ON orders (customer_id);` in the migration set."
100
+ level = "recommended"
101
+ stage = "commit"
102
+ runs = "scope"
103
+ summary = "Checks that an index, a primary key, or a unique key leads with every foreign key column."
104
+ why = "Deleting a parent row scans the whole child table when the foreign key has no index."
105
+ help = "Create an index that starts with the foreign key column."
106
+
107
+ [[naming.override]]
108
+ paths = ["**/migrations/**"]
109
+ categories = ["directories"]
110
+ allow_digits = true
111
+ reason = "Migration directories use the migration tool timestamp names."
112
+
113
+ [defaults]
114
+ "tools.sqlfluff.dialect" = "postgres"
@@ -0,0 +1,78 @@
1
+ # Postgres
2
+
3
+ Migrations, tables, row security, functions, grants, data migrations, and tests of a Postgres
4
+ database. Use the roles and schemas the application declares.
5
+
6
+ ## Migrations
7
+
8
+ - Create a blank migration with the project's migration command, then edit the generated file.
9
+ - Repair migration history only when the user asks for that task.
10
+ - Make every durable change through a migration. Never use a web console or SQL editor on a remote database.
11
+ - Use idempotent data definition language (DDL) only when the public interface promises repeated execution and you verify existing definitions. Do not hide schema drift with `IF NOT EXISTS`.
12
+
13
+ ## Tables
14
+
15
+ - Use UUIDv7 keys when rows need time order.
16
+ - Use `TIMESTAMPTZ` for timestamps. Mutable rows that need audit columns get `created_at` and
17
+ `updated_at`, which the declared timestamp trigger or write owner updates.
18
+ - Index common filters, ordering columns, and frequent row security predicates.
19
+ - Durable invariants live in the database as generated columns, uniqueness, and check
20
+ constraints, not only in client validation.
21
+
22
+ ## Row-level security
23
+
24
+ - Every policy names its roles with `TO`, unless every role belongs in it.
25
+ - Wrap the identity function of an ownership check in a scalar subquery, so the planner evaluates
26
+ it once per statement instead of once per row.
27
+ - `UPDATE` needs both a `SELECT` policy and an `UPDATE` policy.
28
+
29
+ | Operation | Policy expression |
30
+ | ----------- | ------------------------------------------------------------------------------- |
31
+ | Read rows | `USING` selects visible existing rows |
32
+ | Insert rows | `WITH CHECK` validates the proposed row; an insert policy has no `USING` clause |
33
+ | Update rows | `USING` selects existing rows and `WITH CHECK` validates their new values |
34
+ | Delete rows | `USING` selects rows that may be deleted |
35
+
36
+ - Permissive policies broaden access and restrictive policies add conditions; review how every
37
+ policy for the role combines.
38
+ - Check resource ownership and tenant membership. Check proposed values too, so an update cannot
39
+ move a row into another user's or tenant's scope.
40
+ - Handle an unauthenticated identity explicitly, and limit anonymous access to the rows and
41
+ operations the public API exposes.
42
+ - Authorize from trusted claims and current membership data, never from user-editable template
43
+ metadata, and account for stale claims after a membership change.
44
+ - Grants and row level security (RLS) provide separate controls: restrict table and schema privileges, then limit rows with
45
+ policies. RLS does not cover `TRUNCATE`.
46
+ - A `USING (true)` policy fits only a public or service-only table, and the
47
+ migration says why.
48
+ - A privileged service role still needs explicit table grants, never a bypass for precise ones.
49
+ - A view that clients can read sets `security_invoker = true` on Postgres 15 and later to respect
50
+ the RLS of its tables. Otherwise, revoke access to it or keep it in a private schema.
51
+
52
+ ## Functions and grants
53
+
54
+ - Declare functions `SECURITY INVOKER`, the default. Use `SECURITY DEFINER` only for a function that must
55
+ cross RLS or role boundaries, and its comment says why.
56
+ - Start every object from least privilege: `REVOKE ALL ON <object> FROM PUBLIC` and from broad roles.
57
+ - Grant execute with the full function signature, only to the roles that need it. A trigger
58
+ function gets no direct execute grant.
59
+ - Grant schema usage only to roles that resolve objects in it, `SELECT` only for a real read path,
60
+ and write access by column list.
61
+ - Avoid dynamic SQL. When the operation requires it, quote identifiers and values safely and constrain the
62
+ input domain.
63
+ - Raise exceptions with a meaningful SQLSTATE class when clients or tests branch on it.
64
+
65
+ ## Data migrations
66
+
67
+ Durable rows live in migrations, not in seed files.
68
+
69
+ ## Tests
70
+
71
+ - pgTAP tests cover schema, constraints, grants, triggers, RLS, functions, and other behavior a
72
+ migration owns.
73
+ - Integration tests run against a local database and exercise client flows, auth, and RLS. They
74
+ never mock grants or policies away, because they verify the database behavior.
75
+ - A schema change that another project consumes regenerates the types and updates the dependent
76
+ tests.
77
+
78
+ Name pgTAP files for the table or function under test.
@@ -0,0 +1,138 @@
1
+ {
2
+ "recommended": {
3
+ "package": "eslint-plugin-astro",
4
+ "version": "1.7.0",
5
+ "source": "configs.flat/recommended",
6
+ "blocks": [
7
+ { "parser": false },
8
+ { "files": ["*.astro", "**/*.astro"], "parser": true, "rules": {} },
9
+ { "files": ["**/*.astro/*.js", "*.astro/*.js"], "parser": false, "rules": { "prettier/prettier": "off" } },
10
+ { "files": ["**/*.astro/*.ts", "*.astro/*.ts"], "parser": true, "rules": { "prettier/prettier": "off" } },
11
+ {
12
+ "parser": false,
13
+ "rules": {
14
+ "astro/missing-client-only-directive-value": "error",
15
+ "astro/no-conflict-set-directives": "error",
16
+ "astro/no-deprecated-astro-canonicalurl": "error",
17
+ "astro/no-deprecated-astro-fetchcontent": "error",
18
+ "astro/no-deprecated-astro-resolve": "error",
19
+ "astro/no-deprecated-getentrybyslug": "error",
20
+ "astro/no-unused-define-vars-in-style": "error",
21
+ "astro/valid-compile": "error"
22
+ }
23
+ }
24
+ ]
25
+ },
26
+ "accessibility": {
27
+ "package": "eslint-plugin-astro",
28
+ "version": "1.7.0",
29
+ "source": "configs.flat/jsx-a11y-recommended",
30
+ "blocks": [
31
+ { "parser": false },
32
+ { "files": ["*.astro", "**/*.astro"], "parser": true, "rules": {} },
33
+ { "files": ["**/*.astro/*.js", "*.astro/*.js"], "parser": false, "rules": { "prettier/prettier": "off" } },
34
+ { "files": ["**/*.astro/*.ts", "*.astro/*.ts"], "parser": true, "rules": { "prettier/prettier": "off" } },
35
+ {
36
+ "parser": false,
37
+ "rules": {
38
+ "astro/jsx-a11y/alt-text": "error",
39
+ "astro/jsx-a11y/anchor-ambiguous-text": "off",
40
+ "astro/jsx-a11y/anchor-has-content": "error",
41
+ "astro/jsx-a11y/anchor-is-valid": "error",
42
+ "astro/jsx-a11y/aria-activedescendant-has-tabindex": "error",
43
+ "astro/jsx-a11y/aria-props": "error",
44
+ "astro/jsx-a11y/aria-proptypes": "error",
45
+ "astro/jsx-a11y/aria-role": "error",
46
+ "astro/jsx-a11y/aria-unsupported-elements": "error",
47
+ "astro/jsx-a11y/autocomplete-valid": "error",
48
+ "astro/jsx-a11y/click-events-have-key-events": "error",
49
+ "astro/jsx-a11y/control-has-associated-label": [
50
+ "off",
51
+ {
52
+ "ignoreElements": ["audio", "canvas", "embed", "input", "textarea", "tr", "video"],
53
+ "ignoreRoles": [
54
+ "grid",
55
+ "listbox",
56
+ "menu",
57
+ "menubar",
58
+ "radiogroup",
59
+ "row",
60
+ "tablist",
61
+ "toolbar",
62
+ "tree",
63
+ "treegrid"
64
+ ],
65
+ "includeRoles": ["alert", "dialog"]
66
+ }
67
+ ],
68
+ "astro/jsx-a11y/heading-has-content": "error",
69
+ "astro/jsx-a11y/html-has-lang": "error",
70
+ "astro/jsx-a11y/iframe-has-title": "error",
71
+ "astro/jsx-a11y/img-redundant-alt": "error",
72
+ "astro/jsx-a11y/interactive-supports-focus": [
73
+ "error",
74
+ { "tabbable": ["button", "checkbox", "link", "searchbox", "spinbutton", "switch", "textbox"] }
75
+ ],
76
+ "astro/jsx-a11y/label-has-associated-control": "error",
77
+ "astro/jsx-a11y/label-has-for": "off",
78
+ "astro/jsx-a11y/media-has-caption": "error",
79
+ "astro/jsx-a11y/mouse-events-have-key-events": "error",
80
+ "astro/jsx-a11y/no-access-key": "error",
81
+ "astro/jsx-a11y/no-autofocus": "error",
82
+ "astro/jsx-a11y/no-distracting-elements": "error",
83
+ "astro/jsx-a11y/no-interactive-element-to-noninteractive-role": [
84
+ "error",
85
+ { "tr": ["none", "presentation"], "canvas": ["img"] }
86
+ ],
87
+ "astro/jsx-a11y/no-noninteractive-element-interactions": [
88
+ "error",
89
+ {
90
+ "handlers": [
91
+ "onClick",
92
+ "onError",
93
+ "onLoad",
94
+ "onMouseDown",
95
+ "onMouseUp",
96
+ "onKeyPress",
97
+ "onKeyDown",
98
+ "onKeyUp"
99
+ ],
100
+ "alert": ["onKeyUp", "onKeyDown", "onKeyPress"],
101
+ "body": ["onError", "onLoad"],
102
+ "dialog": ["onKeyUp", "onKeyDown", "onKeyPress"],
103
+ "iframe": ["onError", "onLoad"],
104
+ "img": ["onError", "onLoad"]
105
+ }
106
+ ],
107
+ "astro/jsx-a11y/no-noninteractive-element-to-interactive-role": [
108
+ "error",
109
+ {
110
+ "ul": ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
111
+ "ol": ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"],
112
+ "li": ["menuitem", "menuitemradio", "menuitemcheckbox", "option", "row", "tab", "treeitem"],
113
+ "table": ["grid"],
114
+ "td": ["gridcell"],
115
+ "fieldset": ["radiogroup", "presentation"]
116
+ }
117
+ ],
118
+ "astro/jsx-a11y/no-noninteractive-tabindex": [
119
+ "error",
120
+ { "tags": [], "roles": ["tabpanel"], "allowExpressionValues": true }
121
+ ],
122
+ "astro/jsx-a11y/no-redundant-roles": "error",
123
+ "astro/jsx-a11y/no-static-element-interactions": [
124
+ "error",
125
+ {
126
+ "allowExpressionValues": true,
127
+ "handlers": ["onClick", "onMouseDown", "onMouseUp", "onKeyPress", "onKeyDown", "onKeyUp"]
128
+ }
129
+ ],
130
+ "astro/jsx-a11y/role-has-required-aria-props": "error",
131
+ "astro/jsx-a11y/role-supports-aria-props": "error",
132
+ "astro/jsx-a11y/scope": "error",
133
+ "astro/jsx-a11y/tabindex-no-positive": "error"
134
+ }
135
+ }
136
+ ]
137
+ }
138
+ }
@@ -0,0 +1,95 @@
1
+
2
+
3
+ [configuration]
4
+ title = "Astro"
5
+ requires = ["javascript"]
6
+ suggests = ["typescript", "css"]
7
+ description = "Astro components: ESLint reads the frontmatter, the markup, and the scripts together with the recommended and accessibility rules of the Astro plugin as errors, and astro check reports type errors in every component."
8
+
9
+ [detect]
10
+ dependencies = ["astro"]
11
+ extensions = [".astro"]
12
+
13
+ [files]
14
+ extensions = [".astro"]
15
+ filenames = ["astro.config.mjs", "astro.config.ts"]
16
+
17
+ [[tool]]
18
+ name = "eslint-plugin-astro"
19
+ rule_prefix = "astro"
20
+ kind = "library"
21
+ version = "1.7.0"
22
+ npm = "eslint-plugin-astro"
23
+
24
+ [tool.eslint]
25
+ extensions = [".astro"]
26
+
27
+ [[tool]]
28
+ name = "eslint-plugin-jsx-a11y"
29
+
30
+ [[tool]]
31
+ name = "astro-check"
32
+ version = "0.9.10"
33
+ npm = "@astrojs/check"
34
+ version_command = ["astro", "check", "--version"]
35
+
36
+ [[tool]]
37
+ name = "prettier-plugin-astro"
38
+ kind = "library"
39
+ version = "1.0.1"
40
+ npm = "prettier-plugin-astro"
41
+
42
+ [tool.prettier]
43
+ entry = "dist/index.js"
44
+ extensions = [".astro"]
45
+
46
+ [[tool.prettier.overrides]]
47
+ files = "*.astro"
48
+
49
+ [tool.prettier.overrides.options]
50
+ parser = "astro"
51
+
52
+ [[check]]
53
+ name = "astro-check"
54
+ example = "A component whose frontmatter holds `const count: number = 'three';` reports `ts(2322)`. Give the value the declared type, such as `const count: number = 3;`."
55
+ level = "recommended"
56
+ stage = "push"
57
+ runs = "scope"
58
+ cwd = "scope"
59
+ command = ["astro-check", "astro", "check", "--minimumSeverity", "warning", "--minimumFailingSeverity", "warning"]
60
+ exit_codes = [1]
61
+ summary = "Runs astro check over the scope: the type errors and warnings of every Astro component."
62
+ why = "The TypeScript compiler cannot read a component file, so a type error inside one ships unless the Astro checker reads it."
63
+ help = "Read the first error astro check prints and fix that component; later errors are often the same mistake."
64
+
65
+ [check.files]
66
+ extensions = [".astro"]
67
+
68
+ [check.output]
69
+ format = "regex"
70
+ pattern = "^(?<file>[^:\\s][^:]*\\.astro):(?<line>\\d+):(?<column>\\d+) - (?:error|warning) (?<rule>[a-z-]+\\(\\d+\\)): (?<message>.*)$"
71
+
72
+ [eslint]
73
+ all_level_rules = ["astro/no-unused-css-selector", "astro/prefer-class-list-directive", "astro/prefer-object-class-list", "astro/prefer-split-class-list"]
74
+
75
+ [eslint.presets.recommended]
76
+ package = "eslint-plugin-astro"
77
+ source = "configs.flat/recommended"
78
+
79
+ [eslint.presets.accessibility]
80
+ package = "eslint-plugin-astro"
81
+ source = "configs.flat/jsx-a11y-recommended"
82
+
83
+ [[eslint.rules_off]]
84
+ rules = ["gspot/no-trivial-files"]
85
+ files = ["**/*.astro", "**/*.astro/*.ts", "**/*.astro/*.js"]
86
+ reason = "A component is markup, never a file that only forwards: a script that only imports a module is how Astro bundles client code, and Astro reads the props of a component from the Props type in its own frontmatter."
87
+
88
+ [defaults]
89
+ "tools.eslint.import_extension" = "extensionless"
90
+
91
+ [[tool_file]]
92
+ target = ".gspot/config/eslint.config.mjs"
93
+ tool = "eslint"
94
+ fragment = true
95
+ required_tools = ["eslint-plugin-astro", "eslint-plugin-jsx-a11y"]
@@ -0,0 +1,39 @@
1
+ # Astro
2
+
3
+ These rules cover Astro components, islands, markup, routing, and the data a page loads.
4
+
5
+ ## Components
6
+
7
+ <!-- level: all -->
8
+
9
+ - Keep the frontmatter to data: read the props, load what the page needs, and compute values.
10
+ Move a helper longer than a few lines into a module.
11
+
12
+ ## Frontmatter and props
13
+
14
+ - Declare the props as `type Props = { … }` in the frontmatter and read them from `Astro.props`.
15
+ - The frontmatter runs on the server. A value reaches the browser only through the markup or the props of an island.
16
+
17
+ ## Islands
18
+
19
+ - Ship HTML by default. Add a `client:` directive only to a component that needs the browser.
20
+ - Pick the most deferred directive that works: `client:visible` or `client:idle` before `client:load`.
21
+ - `client:only` names the framework of the component, such as `client:only="react"`.
22
+ - Astro serializes island props, so pass plain data. A function or a class instance does not survive the trip.
23
+
24
+ ## Markup
25
+
26
+ - Load a local image through `astro:assets`.
27
+ - Astro bundles a `<script>` without attributes into a module. Keep `is:inline` for code that must run before the page renders.
28
+
29
+ ## Routing and data
30
+
31
+ - A page under `src/pages/` names its route by its path. A dynamic route of a static site exports `getStaticPaths`.
32
+ - Load content through a content collection with a schema, so a missing field fails the build.
33
+ - Read a secret on the server only, from a variable without the `PUBLIC_` prefix. Every visitor can read a `PUBLIC_` variable.
34
+
35
+ ## References
36
+
37
+ - [Astro components](https://docs.astro.build/en/basics/astro-components/).
38
+ - [Client directives](https://docs.astro.build/en/reference/directives-reference/#client-directives).
39
+ - [Content collections](https://docs.astro.build/en/guides/content-collections/).
@@ -0,0 +1,43 @@
1
+ [configuration]
2
+ title = "Expo"
3
+ requires = ["react-native"]
4
+ description = "Expo applications: rules for public environment variables and DOM exports, plus Expo Doctor's project and SDK compatibility checks. React Native supplies the native component and style rules."
5
+
6
+ [detect]
7
+ dependencies = ["expo"]
8
+
9
+ [files]
10
+ filenames = ["app.json", "app.config.js", "app.config.ts", "eas.json"]
11
+
12
+ [[tool]]
13
+ name = "eslint-plugin-expo"
14
+ rule_prefix = "expo"
15
+ kind = "library"
16
+ version = "1.1.0"
17
+ npm = "eslint-plugin-expo"
18
+
19
+ [[tool]]
20
+ name = "expo-doctor"
21
+ version = "1.20.4"
22
+ npm = "expo-doctor"
23
+
24
+ [[check]]
25
+ name = "doctor"
26
+ tool = "expo-doctor"
27
+ example = "An Expo project whose installed `react-native` differs from the version its Expo SDK expects reports a failed Doctor check. Install the expected version with `npx expo install --check` and rerun."
28
+ level = "recommended"
29
+ stage = "push"
30
+ runs = "scope"
31
+ needs = ["network"]
32
+ summary = "Runs Expo Doctor in the selected Expo scope and reports each failed check with its issues."
33
+ why = "A native package on the wrong version for the Expo SDK builds and then crashes on the device."
34
+ help = "Read the issues and advice Expo Doctor prints for the failed check, then run npx expo install --check."
35
+
36
+ [eslint]
37
+ all_level_rules = ["expo/prefer-box-shadow"]
38
+
39
+ [[tool_file]]
40
+ target = ".gspot/config/eslint.config.mjs"
41
+ tool = "eslint"
42
+ fragment = true
43
+ required_tools = ["eslint-plugin-expo"]
@@ -0,0 +1,17 @@
1
+ [configuration]
2
+ title = "Express"
3
+ requires = ["javascript"]
4
+ suggests = ["openapi"]
5
+ description = "An Express service: the Semgrep rules for request handling, an OpenAPI document that lints and matches its source."
6
+
7
+ [detect]
8
+ dependencies = ["express"]
9
+
10
+ [[eslint.selectors]]
11
+ selector = "CallExpression[callee.property.name=/^(json|send)$/] ObjectExpression > Property[key.name=/^(message|error)$/] > TemplateLiteral.value[expressions.length>0]"
12
+ message = "A message a client reads names no identifier; put the value in its own field."
13
+
14
+ [[tool_file]]
15
+ target = ".gspot/config/eslint.config.mjs"
16
+ tool = "eslint"
17
+ fragment = true
@@ -0,0 +1,73 @@
1
+ # Express
2
+
3
+ Routes, middleware, errors, and body parsing in an Express service, on top of the HTTP rules.
4
+
5
+ ## Routes and middleware
6
+
7
+ <!-- level: all -->
8
+
9
+ Express requires no layout, schema library, or response envelope. Use the contracts the project
10
+ already has instead of adding competing ones.
11
+
12
+ ```ts
13
+ // Bad: the route validates by hand, queries the database, and relays a provider response.
14
+ router.post('/orders', async (req, res) => {
15
+ const { accountId, itemIds } = req.body;
16
+ if (!Array.isArray(itemIds)) {
17
+ res.status(400).json({ error: 'Bad items' });
18
+ return;
19
+ }
20
+ const account = await db.accounts.find(accountId);
21
+ const response = await fetch(paymentUrl, { method: 'POST', body: JSON.stringify({ account, itemIds }) });
22
+ res.json(await response.json());
23
+ });
24
+ ```
25
+
26
+ ```ts
27
+ // Good: middleware validates and authenticates; the route makes one domain call.
28
+ router.post('/orders', authenticate, validateBody(submitOrderSchema), async (req, res) => {
29
+ const order = await submitOrder(res.locals.accountId, res.locals.body);
30
+ res.status(201).json(order);
31
+ });
32
+ ```
33
+
34
+ - Middleware can enforce ownership. It reaches the database for product behavior only through auth
35
+ or ownership helpers built for that boundary.
36
+ - A permission check that depends on domain state belongs in a module; a transport-level one
37
+ belongs in ownership middleware.
38
+
39
+ ## Errors
40
+
41
+ - Express 5 forwards the rejection of a promise that a route or middleware returns, so return the
42
+ promise and add no wrapper that repeats this. Express 4 needs the project's async handler or an
43
+ explicit `next(error)`.
44
+ - Callback-based asynchronous work passes its errors to `next(error)` in both versions.
45
+ - Error middleware keeps its four-argument signature, even when the function does not use an argument.
46
+ - After the response sends its headers, error middleware calls `next(error)` instead of writing a
47
+ second response.
48
+ - The not-found handler comes after every route.
49
+
50
+ ## Body parsing and proxies
51
+
52
+ Parsing a body takes work, so mount a JSON parser with a small default limit, and a larger one only on
53
+ the route that needs it.
54
+
55
+ ```ts
56
+ // Bad: every endpoint inherits an oversized JSON parser.
57
+ app.use(express.json({ limit: '50mb' }));
58
+
59
+ // Good: a larger limit only where the endpoint needs it.
60
+ app.use('/imports', express.json({ limit: '5mb' }), importsRouter);
61
+ app.use(express.json({ limit: '100kb' }));
62
+ ```
63
+
64
+ Enable `trust proxy` only when you know the deployment topology. Select the proxies whose
65
+ forwarding headers you trust.
66
+
67
+ ## References
68
+
69
+ | Topic | Primary source |
70
+ | -------------- | -------------------------------------------------------------------------------- |
71
+ | Error handling | [Express error handling](https://expressjs.com/en/guide/error-handling/) |
72
+ | Security | [Production security](https://expressjs.com/en/advanced/best-practice-security/) |
73
+ | Proxies | [Express behind proxies](https://expressjs.com/en/guide/behind-proxies.html) |