@gspothq/cli 0.0.0-stage → 0.1.1
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.
- package/README.md +18 -2
- package/configurations/database/postgres/manifest.toml +114 -0
- package/configurations/database/postgres/rules/POSTGRES.md +78 -0
- package/configurations/framework/astro/eslint-presets.json +138 -0
- package/configurations/framework/astro/manifest.toml +95 -0
- package/configurations/framework/astro/rules/ASTRO.md +39 -0
- package/configurations/framework/expo/manifest.toml +43 -0
- package/configurations/framework/express/manifest.toml +17 -0
- package/configurations/framework/express/rules/EXPRESS.md +73 -0
- package/configurations/framework/express/semgrep/express.yml +71 -0
- package/configurations/framework/fastapi/manifest.toml +11 -0
- package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
- package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
- package/configurations/framework/nestjs/eslint-presets.json +62 -0
- package/configurations/framework/nestjs/manifest.toml +52 -0
- package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
- package/configurations/framework/nextjs/eslint-presets.json +73 -0
- package/configurations/framework/nextjs/manifest.toml +119 -0
- package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
- package/configurations/framework/react/eslint-presets.json +77 -0
- package/configurations/framework/react/manifest.toml +93 -0
- package/configurations/framework/react/rules/REACT.md +108 -0
- package/configurations/framework/react-dom/eslint-presets.json +110 -0
- package/configurations/framework/react-dom/manifest.toml +24 -0
- package/configurations/framework/react-native/manifest.toml +46 -0
- package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
- package/configurations/framework/site/manifest.toml +170 -0
- package/configurations/framework/site/rules/SITE.md +20 -0
- package/configurations/framework/svelte/eslint-presets.json +67 -0
- package/configurations/framework/svelte/manifest.toml +83 -0
- package/configurations/framework/svelte/rules/SVELTE.md +48 -0
- package/configurations/framework/vue/eslint-presets.json +180 -0
- package/configurations/framework/vue/manifest.toml +83 -0
- package/configurations/framework/vue/rules/VUE.md +41 -0
- package/configurations/general/commits/manifest.toml +111 -0
- package/configurations/general/commits/rules/GIT.md +31 -0
- package/configurations/general/coverage/manifest.toml +3 -0
- package/configurations/general/dependencies/manifest.toml +159 -0
- package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
- package/configurations/general/dependencies/syncpack.json +15 -0
- package/configurations/general/docs/manifest.toml +79 -0
- package/configurations/general/docs/rules/DOCS.md +86 -0
- package/configurations/general/duplication/manifest.toml +32 -0
- package/configurations/general/engineering/manifest.toml +8 -0
- package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
- package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
- package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
- package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
- package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
- package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
- package/configurations/general/engineering/rules/code/CLI.md +37 -0
- package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
- package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
- package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
- package/configurations/general/engineering/rules/code/HTTP.md +46 -0
- package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
- package/configurations/general/engineering/rules/code/TESTING.md +58 -0
- package/configurations/general/files/manifest.toml +141 -0
- package/configurations/general/files/rules/YAML.md +19 -0
- package/configurations/general/format/manifest.toml +152 -0
- package/configurations/general/gspot/manifest.toml +65 -0
- package/configurations/general/gspot/rules/GENERATED.md +10 -0
- package/configurations/general/licenses/manifest.toml +33 -0
- package/configurations/general/naming/defaults.json +423 -0
- package/configurations/general/naming/manifest.toml +35 -0
- package/configurations/general/naming/rules/NAMING.md +179 -0
- package/configurations/general/prose/manifest.toml +86 -0
- package/configurations/general/prose/rules/WRITING.md +31 -0
- package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
- package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
- package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
- package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
- package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
- package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
- package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
- package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
- package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
- package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
- package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
- package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
- package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
- package/configurations/general/repository/manifest.toml +24 -0
- package/configurations/general/secrets/gitleaks.toml +2 -0
- package/configurations/general/secrets/manifest.toml +125 -0
- package/configurations/general/secrets/rules/SECRETS.md +13 -0
- package/configurations/general/security/manifest.toml +77 -0
- package/configurations/general/security/rules/SECURITY.md +55 -0
- package/configurations/general/spelling/manifest.toml +46 -0
- package/configurations/general/structure/manifest.toml +70 -0
- package/configurations/infrastructure/ansible/manifest.toml +33 -0
- package/configurations/infrastructure/docker/manifest.toml +127 -0
- package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
- package/configurations/infrastructure/docker/trivyignore +2 -0
- package/configurations/infrastructure/github-actions/manifest.toml +76 -0
- package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
- package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
- package/configurations/infrastructure/nginx/manifest.toml +51 -0
- package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
- package/configurations/infrastructure/openapi/manifest.toml +50 -0
- package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
- package/configurations/infrastructure/xcode/manifest.toml +131 -0
- package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
- package/configurations/language/bash/manifest.toml +261 -0
- package/configurations/language/bash/rules/BASH.md +106 -0
- package/configurations/language/bash/rules/LANGUAGE.md +213 -0
- package/configurations/language/bash/rules/NAMING.md +62 -0
- package/configurations/language/bash/rules/OPERATIONS.md +77 -0
- package/configurations/language/bash/rules/SAFETY.md +222 -0
- package/configurations/language/bash/semgrep/bash.yml +30 -0
- package/configurations/language/css/manifest.toml +98 -0
- package/configurations/language/css/rules/CSS.md +28 -0
- package/configurations/language/css/rules/NAMING.md +17 -0
- package/configurations/language/css/rules/TAILWIND.md +26 -0
- package/configurations/language/css/stylelint-rule-names.json +89 -0
- package/configurations/language/html/manifest.toml +59 -0
- package/configurations/language/html/rules/HTML.md +27 -0
- package/configurations/language/html/rules/NAMING.md +16 -0
- package/configurations/language/javascript/eslint-presets.json +997 -0
- package/configurations/language/javascript/eslint-rule-names.json +299 -0
- package/configurations/language/javascript/eslint-runtime-names.json +61 -0
- package/configurations/language/javascript/manifest.toml +393 -0
- package/configurations/language/javascript/rules/BUN.md +13 -0
- package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
- package/configurations/language/javascript/rules/DENO.md +15 -0
- package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
- package/configurations/language/javascript/rules/NAMING.md +31 -0
- package/configurations/language/javascript/rules/NODE.md +21 -0
- package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
- package/configurations/language/javascript/semgrep/javascript.yml +84 -0
- package/configurations/language/markdown/manifest.toml +85 -0
- package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
- package/configurations/language/python/manifest.toml +621 -0
- package/configurations/language/python/rules/DESIGN.md +136 -0
- package/configurations/language/python/rules/FLOW.md +124 -0
- package/configurations/language/python/rules/NAMING.md +53 -0
- package/configurations/language/python/rules/PACKAGING.md +49 -0
- package/configurations/language/python/rules/PYTHON.md +136 -0
- package/configurations/language/python/rules/TYPING.md +132 -0
- package/configurations/language/sql/manifest.toml +68 -0
- package/configurations/language/sql/rules/NAMING.md +90 -0
- package/configurations/language/sql/rules/SQL.md +9 -0
- package/configurations/language/swift/manifest.toml +164 -0
- package/configurations/language/swift/rules/NAMING.md +88 -0
- package/configurations/language/swift/rules/SWIFT.md +134 -0
- package/configurations/language/swift/rules/SWIFTUI.md +61 -0
- package/configurations/language/swift/rules/UIKIT.md +99 -0
- package/configurations/language/swift/semgrep/swift.yml +126 -0
- package/configurations/language/typescript/eslint-presets.json +215 -0
- package/configurations/language/typescript/manifest.toml +72 -0
- package/configurations/language/typescript/rules/NAMING.md +73 -0
- package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
- package/configurations/library/drizzle/manifest.toml +68 -0
- package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
- package/configurations/library/react-hook-form/manifest.toml +15 -0
- package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
- package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
- package/configurations/library/tanstack-query/eslint-presets.json +21 -0
- package/configurations/library/tanstack-query/manifest.toml +26 -0
- package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
- package/configurations/library/testing-library/eslint-presets.json +191 -0
- package/configurations/library/testing-library/manifest.toml +47 -0
- package/configurations/library/translations/manifest.toml +40 -0
- package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
- package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
- package/configurations/library/trpc/manifest.toml +14 -0
- package/configurations/library/trpc/rules/TRPC.md +43 -0
- package/configurations/library/zod/manifest.toml +25 -0
- package/configurations/library/zod/rules/ZOD.md +44 -0
- package/configurations/library/zustand/manifest.toml +18 -0
- package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
- package/configurations/platform/cloudflare/manifest.toml +94 -0
- package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
- package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
- package/configurations/platform/supabase/manifest.toml +112 -0
- package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
- package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
- package/configurations/testing/jest/manifest.toml +54 -0
- package/configurations/testing/pytest/manifest.toml +53 -0
- package/configurations/testing/swift-tests/manifest.toml +52 -0
- package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
- package/configurations/testing/vitest/eslint-presets.json +22 -0
- package/configurations/testing/vitest/manifest.toml +69 -0
- package/configurations/testing/vitest/rules/VITEST.md +18 -0
- package/dist/LICENSE.md +201 -0
- package/dist/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/eslint-rule-levels.json +10 -0
- package/dist/gspot.js +30740 -0
- package/dist/linkinator-program.js +65 -0
- package/dist/purgecss-program.js +51 -0
- package/dist/tool-versions.json +3 -0
- package/grammars/bash.wasm +0 -0
- package/grammars/css.wasm +0 -0
- package/grammars/html.wasm +0 -0
- package/grammars/javascript.wasm +0 -0
- package/grammars/licenses/tree-sitter-bash.txt +21 -0
- package/grammars/licenses/tree-sitter-css.txt +21 -0
- package/grammars/licenses/tree-sitter-html.txt +21 -0
- package/grammars/licenses/tree-sitter-javascript.txt +21 -0
- package/grammars/licenses/tree-sitter-python.txt +21 -0
- package/grammars/licenses/tree-sitter-swift.txt +21 -0
- package/grammars/licenses/tree-sitter-typescript.txt +21 -0
- package/grammars/python.wasm +0 -0
- package/grammars/swift.wasm +0 -0
- package/grammars/tsx.wasm +0 -0
- package/grammars/typescript.wasm +0 -0
- package/gspot.schema.json +45991 -0
- package/package.json +93 -5
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
allowed_words = ["SQL"]
|
|
2
|
+
[configuration]
|
|
3
|
+
title = "SQL"
|
|
4
|
+
suggests = []
|
|
5
|
+
description = "SQLFluff checks layout and style in the project dialect. At all, SQL checks line comments, file length, and PostgreSQL function structure."
|
|
6
|
+
|
|
7
|
+
[files]
|
|
8
|
+
extensions = [".sql", ".pgsql", ".psql"]
|
|
9
|
+
|
|
10
|
+
[[tool]]
|
|
11
|
+
name = "sqlfluff"
|
|
12
|
+
crash_pattern = '^Traceback \(most recent call last\):$'
|
|
13
|
+
suppression = { marker = "\\bnoqa\\b", reason = "--[^\\n]*?-- ?(?<reason>\\S.*)" }
|
|
14
|
+
version = "4.3.0"
|
|
15
|
+
min_version = "3.0.0"
|
|
16
|
+
pypi = "sqlfluff"
|
|
17
|
+
mise = "pipx:sqlfluff"
|
|
18
|
+
brew = "sqlfluff"
|
|
19
|
+
[[tool.replace]]
|
|
20
|
+
file = ".sqlfluff"
|
|
21
|
+
shared = false
|
|
22
|
+
|
|
23
|
+
[[tool.replace]]
|
|
24
|
+
file = ".sqlfluffignore"
|
|
25
|
+
shared = false
|
|
26
|
+
|
|
27
|
+
[[tool.replace]]
|
|
28
|
+
file = "pyproject.toml"
|
|
29
|
+
table = "tool.sqlfluff"
|
|
30
|
+
shared = true
|
|
31
|
+
|
|
32
|
+
[[tool.replace]]
|
|
33
|
+
file = "setup.cfg"
|
|
34
|
+
table = "sqlfluff"
|
|
35
|
+
shared = true
|
|
36
|
+
|
|
37
|
+
[[tool.replace]]
|
|
38
|
+
file = "tox.ini"
|
|
39
|
+
table = "sqlfluff"
|
|
40
|
+
shared = true
|
|
41
|
+
|
|
42
|
+
[[tool_file]]
|
|
43
|
+
target = ".gspot/config/sqlfluff.cfg"
|
|
44
|
+
tool = "sqlfluff"
|
|
45
|
+
per_scope = true
|
|
46
|
+
rule_keys = ["sqlfluff.exclude_rules", "sqlfluff:rules"]
|
|
47
|
+
|
|
48
|
+
[[check]]
|
|
49
|
+
name = "sqlfluff"
|
|
50
|
+
example = "The query `select id from user_accounts;` reports CP01 under the configured uppercase keyword policy. Format it as `SELECT id FROM user_accounts;` and rerun using the project dialect."
|
|
51
|
+
level = "recommended"
|
|
52
|
+
stage = "commit"
|
|
53
|
+
command = ["sqlfluff", "lint", "--config", "{tool_file:sqlfluff}", "--ignore-local-config", "--disable-progress-bar", "--format", "json", "{files}"]
|
|
54
|
+
exit_codes = [1]
|
|
55
|
+
fix = ["sqlfluff", "fix", "--config", "{tool_file:sqlfluff}", "--ignore-local-config", "--disable-progress-bar", "{files}"]
|
|
56
|
+
output = { format = "json", children = "violations", fields = { file = "filepath", line = "start_line_no", column = "start_line_pos", rule = "code", message = "description" } }
|
|
57
|
+
summary = "Lints every SQL file for layout, capitalization, aliasing, and ambiguous references, in the dialect the database configuration names."
|
|
58
|
+
why = "SQL that reads one way in every file lets a reviewer look at what the statement does."
|
|
59
|
+
help = "Run gspot check --fix. Turn one rule off with gspot ignore sql/sqlfluff --rule `<code>` --reason \"`<why>`\"."
|
|
60
|
+
|
|
61
|
+
[[check]]
|
|
62
|
+
name = "functions"
|
|
63
|
+
example = "A SQL function that only forwards one expression reports a trivial-function finding. Inline unnecessary calls and delete the function. If an external API requires the signature, record that with `gspot ignore` and a reason."
|
|
64
|
+
level = "all"
|
|
65
|
+
stage = "commit"
|
|
66
|
+
summary = "For the postgres dialect, reports trivial SQL and PL/pgSQL functions and excessive declared input parameters."
|
|
67
|
+
why = "Unnecessary functions add indirection; excessive parameters obscure responsibilities."
|
|
68
|
+
help = "Inline unnecessary functions. If a required API needs one, run `gspot ignore sql/functions --paths <file> --reason <why>`. Reduce parameters or configure limits.sql.function_parameters."
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# SQL Naming
|
|
2
|
+
|
|
3
|
+
SQL names define durable infrastructure interfaces. Rename them only
|
|
4
|
+
through migrations and contract-aware code changes.
|
|
5
|
+
|
|
6
|
+
## SQL case rules
|
|
7
|
+
|
|
8
|
+
<!-- level: all -->
|
|
9
|
+
|
|
10
|
+
- SQL schemas, tables, columns, functions, function parameters, indexes,
|
|
11
|
+
triggers, constraints, and policies use `lower_snake_case`.
|
|
12
|
+
- Fully qualify grants and function signatures when names have more than one possible meaning.
|
|
13
|
+
- Do not use quoted sentence-style identifiers for local policy names.
|
|
14
|
+
- Do not use pluralization or prefixes inconsistently inside one schema.
|
|
15
|
+
|
|
16
|
+
| Avoid | Prefer | Meaning |
|
|
17
|
+
| ---------------------------- | -------------------------------- | ---------------------------------- |
|
|
18
|
+
| `public.OrderItems` | `public.order_items` | An order-item table. |
|
|
19
|
+
| `users_can_view_order_items` | `users_can_view_own_order_items` | A policy restricted to owned rows. |
|
|
20
|
+
|
|
21
|
+
The policy name must describe its actual predicate. A name that says "own"
|
|
22
|
+
does not establish ownership checks or enable Row Level Security.
|
|
23
|
+
|
|
24
|
+
## Migration filenames
|
|
25
|
+
|
|
26
|
+
<!-- level: all -->
|
|
27
|
+
|
|
28
|
+
Follow the migration tool's file names. Name each migration for its durable database change.
|
|
29
|
+
|
|
30
|
+
## SQL tables and columns
|
|
31
|
+
|
|
32
|
+
<!-- level: all -->
|
|
33
|
+
|
|
34
|
+
- Table names identify the domain set stored by the relation.
|
|
35
|
+
- Column names identify the value, not the application layer that reads it.
|
|
36
|
+
- Foreign-key columns use the referenced concept plus `_id`.
|
|
37
|
+
- Timestamp columns use stable event names such as `created_at`,
|
|
38
|
+
`updated_at`, `deleted_at`, or a concrete domain event time.
|
|
39
|
+
- Boolean columns use concise positive assertion names without an `is_`
|
|
40
|
+
prefix.
|
|
41
|
+
- Avoid generic columns that hide meaning.
|
|
42
|
+
|
|
43
|
+
| Avoid | Prefer | Meaning |
|
|
44
|
+
| --------- | --------------------------- | ------------------------- |
|
|
45
|
+
| `data` | `message_delivery_attempts` | Delivery attempt table. |
|
|
46
|
+
| `payload` | `provider_response` | Response payload column. |
|
|
47
|
+
| `flag` | `retryable` | Retry eligibility column. |
|
|
48
|
+
| `date` | `attempted_at` | Attempt timestamp column. |
|
|
49
|
+
|
|
50
|
+
## SQL functions and parameters
|
|
51
|
+
|
|
52
|
+
<!-- level: all -->
|
|
53
|
+
|
|
54
|
+
- SQL function names use verb phrases or domain operation names. These verbs govern function
|
|
55
|
+
names, not migration filenames.
|
|
56
|
+
|
|
57
|
+
- Retrieval functions use `get_` regardless of whether they return one row,
|
|
58
|
+
an optional row, a collection, or a paginated collection. Do not use
|
|
59
|
+
`read_`, `find_`, `fetch_`, `load_`, or `list_` as retrieval synonyms.
|
|
60
|
+
- Data-access mutation functions use `set_`, `insert_`, `update_`, or
|
|
61
|
+
`delete_` according to the operation they perform. Do not use `create_`,
|
|
62
|
+
`add_`, `save_`, `write_`, `put_`, `upsert_`, or `remove_` as synonyms.
|
|
63
|
+
- Function parameters use `lower_snake_case`.
|
|
64
|
+
- Parameter names never collide with table column names.
|
|
65
|
+
- `SECURITY DEFINER` functions must have names that make the privileged action
|
|
66
|
+
clear.
|
|
67
|
+
- Do not name functions like arbitrary script tasks.
|
|
68
|
+
|
|
69
|
+
Use `archive_expired_orders` for a transactional operation that archives
|
|
70
|
+
expired orders. Name its user argument `target_user_id` when `user_id` already names
|
|
71
|
+
already a column in the affected tables. A generic `run` name hides the
|
|
72
|
+
operation, and an unqualified `id` can obscure column ownership.
|
|
73
|
+
|
|
74
|
+
## Indexes, constraints, triggers, and policies
|
|
75
|
+
|
|
76
|
+
<!-- level: all -->
|
|
77
|
+
|
|
78
|
+
- Name constraints and scope them to their table or concept.
|
|
79
|
+
- Name indexes by table plus indexed columns or purpose.
|
|
80
|
+
- Name triggers by event and action.
|
|
81
|
+
- Name policies by actor plus allowed action.
|
|
82
|
+
- Keep names stable because they appear in migrations, errors, grants, and
|
|
83
|
+
database inspection output.
|
|
84
|
+
|
|
85
|
+
| Avoid | Prefer | Meaning |
|
|
86
|
+
| --------------- | --------------------------------- | ------------------------------------ |
|
|
87
|
+
| `idx1` | `messages_user_id_created_at_idx` | Index on user and creation time. |
|
|
88
|
+
| `check_status` | `messages_status_not_empty_check` | Nonempty status constraint. |
|
|
89
|
+
| `trigger1` | `messages_set_updated_at` | Update timestamp trigger. |
|
|
90
|
+
| `select_policy` | `users_can_view_own_messages` | Policy restricted to owned messages. |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# SQL
|
|
2
|
+
|
|
3
|
+
## SQL style
|
|
4
|
+
|
|
5
|
+
- Fully qualify cross-schema references.
|
|
6
|
+
- Prefer explicit column lists for every `INSERT` and every grant of write
|
|
7
|
+
privileges.
|
|
8
|
+
- Keep table constraints close to the table definition unless the migration must add them
|
|
9
|
+
later for dependency reasons.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
allowed_words = ["Swift", "SwiftLint", "SwiftFormat", "Periphery"]
|
|
2
|
+
|
|
3
|
+
ignored_folders = [".build", "DerivedData", "Pods"]
|
|
4
|
+
|
|
5
|
+
[configuration]
|
|
6
|
+
title = "Swift"
|
|
7
|
+
suggests = []
|
|
8
|
+
description = "Swift sources: SwiftLint with stable rules selected by level and analyzer rules, SwiftFormat, a build, and Periphery for dead code."
|
|
9
|
+
|
|
10
|
+
[detect]
|
|
11
|
+
extensions = [".swift"]
|
|
12
|
+
project_files = ["Package.swift"]
|
|
13
|
+
|
|
14
|
+
[files]
|
|
15
|
+
extensions = [".swift"]
|
|
16
|
+
filenames = ["Package.swift", "Package.resolved"]
|
|
17
|
+
|
|
18
|
+
[[tool]]
|
|
19
|
+
name = "swiftlint"
|
|
20
|
+
rule_url = "https://realm.github.io/SwiftLint/{rule}.html"
|
|
21
|
+
suppression = { marker = "\\bswiftlint:disable\\b", reason = " - (?<reason>\\S.*)" }
|
|
22
|
+
builds = ["macos", "linux"]
|
|
23
|
+
version = "0.63.2"
|
|
24
|
+
min_version = "0.57.0"
|
|
25
|
+
mise = "swiftlint"
|
|
26
|
+
brew = "swiftlint"
|
|
27
|
+
github = { name = "realm/SwiftLint", version = "0.63.2" }
|
|
28
|
+
version_command = ["version"]
|
|
29
|
+
[[tool.replace]]
|
|
30
|
+
file = ".swiftlint.yml"
|
|
31
|
+
shared = false
|
|
32
|
+
|
|
33
|
+
[[tool.replace]]
|
|
34
|
+
file = ".swiftlint.yaml"
|
|
35
|
+
shared = false
|
|
36
|
+
|
|
37
|
+
[[tool]]
|
|
38
|
+
name = "swiftformat"
|
|
39
|
+
suppression = { marker = "\\bswiftformat:disable\\b", reason = " - (?<reason>\\S.*)" }
|
|
40
|
+
builds = ["macos", "linux"]
|
|
41
|
+
version = "0.61.1"
|
|
42
|
+
min_version = "0.54.0"
|
|
43
|
+
# The GitHub release ships an app bundle beside the executable, which mise skips.
|
|
44
|
+
mise = { name = "swiftformat", version = "0.61.1", options = { no_app = true, rename_exe = "swiftformat" } }
|
|
45
|
+
brew = "swiftformat"
|
|
46
|
+
github = { name = "nicklockwood/SwiftFormat", version = "0.61.1" }
|
|
47
|
+
[[tool.replace]]
|
|
48
|
+
file = ".swiftformat"
|
|
49
|
+
shared = false
|
|
50
|
+
|
|
51
|
+
[[tool]]
|
|
52
|
+
name = "periphery"
|
|
53
|
+
suppression = { marker = "\\bperiphery:ignore\\b", reason = " - (?<reason>\\S.*)" }
|
|
54
|
+
builds = ["macos"]
|
|
55
|
+
version = "3.6.0"
|
|
56
|
+
min_version = "3.0.0"
|
|
57
|
+
mise = "periphery"
|
|
58
|
+
brew = "periphery"
|
|
59
|
+
github = { name = "peripheryapp/periphery", version = "3.6.0" }
|
|
60
|
+
version_command = ["version"]
|
|
61
|
+
[[tool.replace]]
|
|
62
|
+
file = ".periphery.yml"
|
|
63
|
+
shared = false
|
|
64
|
+
|
|
65
|
+
[[tool.replace]]
|
|
66
|
+
file = ".periphery.yaml"
|
|
67
|
+
shared = false
|
|
68
|
+
|
|
69
|
+
[[tool]]
|
|
70
|
+
name = "swift"
|
|
71
|
+
system = true
|
|
72
|
+
|
|
73
|
+
[[tool]]
|
|
74
|
+
name = "xcodebuild"
|
|
75
|
+
install_hint = "install Xcode from the App Store"
|
|
76
|
+
system = true
|
|
77
|
+
|
|
78
|
+
[[tool_file]]
|
|
79
|
+
target = ".gspot/config/swiftlint.yml"
|
|
80
|
+
tool = "swiftlint"
|
|
81
|
+
rule_keys = ["opt_in_rules", "analyzer_rules", "disabled_rules"]
|
|
82
|
+
per_scope = true
|
|
83
|
+
pointer = { path = ".swiftlint.yml", body = "parent_config: {target_json}\n" }
|
|
84
|
+
|
|
85
|
+
[[tool_file]]
|
|
86
|
+
target = ".gspot/config/swiftformat"
|
|
87
|
+
tool = "swiftformat"
|
|
88
|
+
rule_keys = ["enable", "disable"]
|
|
89
|
+
per_scope = true
|
|
90
|
+
|
|
91
|
+
[[tool_file]]
|
|
92
|
+
target = ".gspot/config/periphery.yml"
|
|
93
|
+
tool = "periphery"
|
|
94
|
+
per_scope = true
|
|
95
|
+
|
|
96
|
+
[[check]]
|
|
97
|
+
name = "swiftlint"
|
|
98
|
+
example = "The forced cast `value as! NSString` reports `force_cast`. Use a conditional cast and handle the failed conversion, or change the input contract so no cast is required."
|
|
99
|
+
level = "recommended"
|
|
100
|
+
stage = "commit"
|
|
101
|
+
tool = "swiftlint"
|
|
102
|
+
command = ["swiftlint", "lint", "--strict", "--quiet", "--no-cache", "--reporter", "json", "{files}"]
|
|
103
|
+
fix = ["swiftlint", "lint", "--fix", "--quiet", "--no-cache", "{files}"]
|
|
104
|
+
cwd = "scope"
|
|
105
|
+
files = { extensions = [".swift"] }
|
|
106
|
+
output = { format = "json", fields = { file = "file", line = "line", column = "character", rule = "rule_id", message = "reason" } }
|
|
107
|
+
summary = "Lints Swift files with the scoped and nested SwiftLint configurations, treating warnings as errors."
|
|
108
|
+
why = "Forced casts and tries can crash, and a strong delegate can retain its owner. Find these before committing."
|
|
109
|
+
help = "Run gspot check --fix for the rules that fix themselves. Turn one rule off with gspot ignore swift/swiftlint --rule `<id>` --reason \"`<why>`\"."
|
|
110
|
+
|
|
111
|
+
[[check]]
|
|
112
|
+
name = "swiftformat"
|
|
113
|
+
example = "A declaration beginning `func greeting` reports `consecutiveSpaces`. Run the configured formatter to obtain `func greeting`, inspect the diff, and rerun."
|
|
114
|
+
level = "recommended"
|
|
115
|
+
stage = "commit"
|
|
116
|
+
command = ["swiftformat", "--lint", "--config", "{tool_file:swiftformat}", "{files}"]
|
|
117
|
+
exit_codes = [1]
|
|
118
|
+
fix = ["swiftformat", "--quiet", "--config", "{tool_file:swiftformat}", "{files}"]
|
|
119
|
+
files = { extensions = [".swift"] }
|
|
120
|
+
output = { format = "regex", pattern = "^(?<file>.+?):(?<line>\\d+):(?<column>\\d+): (?:error|warning): \\((?<rule>\\w+)\\) (?<message>.*)$" }
|
|
121
|
+
summary = "Checks the layout of every Swift file against the shipped SwiftFormat rules."
|
|
122
|
+
why = "One layout everywhere keeps a diff about the change and not about where a brace went."
|
|
123
|
+
help = "Run gspot check --fix."
|
|
124
|
+
|
|
125
|
+
[[check]]
|
|
126
|
+
name = "build"
|
|
127
|
+
example = "A Swift assignment of a string to an `Int` variable fails compilation. Correct the value or declared type and rerun with the required compiler and project settings available."
|
|
128
|
+
level = "recommended"
|
|
129
|
+
stage = "push"
|
|
130
|
+
runs = "scope"
|
|
131
|
+
needs = ["build"]
|
|
132
|
+
operating_systems = ["macos", "linux"]
|
|
133
|
+
summary = "Builds the package or Xcode scheme incrementally to report compiler errors."
|
|
134
|
+
why = "Swift has no type check without a build, so the build is the type check."
|
|
135
|
+
help = "Fix the first compiler error; later ones are often the same mistake."
|
|
136
|
+
|
|
137
|
+
[[check]]
|
|
138
|
+
name = "swiftlint-analyze"
|
|
139
|
+
example = "An import unused by the compiled Swift sources reports an analyzer finding. Remove the import and rerun the build-backed analysis. A missing build log does not establish a clean result."
|
|
140
|
+
level = "recommended"
|
|
141
|
+
stage = "manual"
|
|
142
|
+
runs = "scope"
|
|
143
|
+
needs = ["build"]
|
|
144
|
+
operating_systems = ["macos"]
|
|
145
|
+
tool = "swiftlint"
|
|
146
|
+
summary = "Runs the SwiftLint analyzer rules over the compiler log of the build: unused imports and captured variables."
|
|
147
|
+
why = "These rules need the compiler's view of the code, which a file-by-file lint does not have."
|
|
148
|
+
help = "Remove what the rule names. Keep an import the analyzer misjudges under tools.swiftlint.always_keep_imports."
|
|
149
|
+
|
|
150
|
+
[[check]]
|
|
151
|
+
name = "periphery"
|
|
152
|
+
example = "A declaration with no references in the selected compiled project reports unused code. Delete the unused declaration and rebuild before rerunning. Runtime-only references require a specific documented exception."
|
|
153
|
+
level = "recommended"
|
|
154
|
+
stage = "push"
|
|
155
|
+
runs = "scope"
|
|
156
|
+
needs = ["build"]
|
|
157
|
+
tool = "periphery"
|
|
158
|
+
summary = "Scans the built project for declarations nothing uses."
|
|
159
|
+
why = "Dead code is read, maintained, and migrated by people who cannot tell it is dead."
|
|
160
|
+
help = "Delete the declaration, or mark it with a periphery:ignore comment that says who uses it."
|
|
161
|
+
|
|
162
|
+
[rules]
|
|
163
|
+
"SWIFTUI.md" = { tags = ["swift-import:SwiftUI"] }
|
|
164
|
+
"UIKIT.md" = { tags = ["swift-import:UIKit"] }
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Swift Naming
|
|
2
|
+
|
|
3
|
+
Swift naming follows the Apple API Design Guidelines and optimizes for call-site clarity.
|
|
4
|
+
|
|
5
|
+
## Case, scope, and files
|
|
6
|
+
|
|
7
|
+
<!-- level: all -->
|
|
8
|
+
|
|
9
|
+
Preserve external names and domain notation that the team uses. Express privacy through access
|
|
10
|
+
modifiers, not an underscore.
|
|
11
|
+
|
|
12
|
+
Express a structural relationship through nesting rather than a longer
|
|
13
|
+
top-level name: `Parser.Error`, not `ParseError`. A static property does not repeat its
|
|
14
|
+
declaring type: `UIColor.primary`, not `UIColor.primaryColor`. Use `shared` and `default`
|
|
15
|
+
only when they describe the role. Use an empty enum as a namespace only for tightly related
|
|
16
|
+
constants or functions never instantiated.
|
|
17
|
+
|
|
18
|
+
## Roles
|
|
19
|
+
|
|
20
|
+
<!-- level: all -->
|
|
21
|
+
|
|
22
|
+
A suffix says what a type owns and means one thing across the whole codebase. If `Repository`
|
|
23
|
+
owns domain-facing data access in one feature, it does not own HTTP mechanics in another.
|
|
24
|
+
Which suffixes a project uses depends on the pattern it picked, but these framework words have
|
|
25
|
+
one meaning:
|
|
26
|
+
|
|
27
|
+
| Role word | Use when |
|
|
28
|
+
| ---------------- | -------------------------------------------------------------------------------------- |
|
|
29
|
+
| `View` | SwiftUI view or UIKit view type that presents UI. |
|
|
30
|
+
| `ViewController` | UIKit screen controller. |
|
|
31
|
+
| `ViewModel` | Presentation state and user intent owner for one screen, flow, or cohesive surface. |
|
|
32
|
+
| `ViewState` | Value describing screen presentation state. |
|
|
33
|
+
| `ViewAction` | Typed user or lifecycle intent emitted by a view. |
|
|
34
|
+
| `Coordinator` | Navigation or flow state owner. |
|
|
35
|
+
| `Router` | Route mutation or destination-selection boundary, narrower than a coordinator. |
|
|
36
|
+
| `DataSource` | UIKit list adapter that feeds rows and sections. |
|
|
37
|
+
| `CellModel` | Values a reusable cell renders: strings, image references, state, and lightweight IDs. |
|
|
38
|
+
|
|
39
|
+
Repository protocols speak domain language and return domain results through domain
|
|
40
|
+
operations such as `TemplateRepository.getTemplate`; an implementation may name its technology
|
|
41
|
+
such as `HTTPTemplateRepository` when that distinguishes real implementations. Client types own
|
|
42
|
+
external API or SDK mechanics such as `TemplateAPIClient`. Coordinators own route state, destination
|
|
43
|
+
construction, stack mutations, and presentation flow such as `TemplateCoordinator.showEditTemplate`.
|
|
44
|
+
Keep route enums within their feature and name them for the flow. Give them stable domain identifiers
|
|
45
|
+
such as `TemplateDestination.editTemplate`. Never give them view models, views, repository implementations, SDK
|
|
46
|
+
clients, database records, or DTOs.
|
|
47
|
+
|
|
48
|
+
## Methods, labels, and delegates
|
|
49
|
+
|
|
50
|
+
<!-- level: all -->
|
|
51
|
+
|
|
52
|
+
When the project uses view models, name a direct UI event for the event
|
|
53
|
+
such as `submitButtonTapped()`, `cameraPermissionDenied()`, `selectedItemChanged(to:)`. Name domain work
|
|
54
|
+
with a domain verb such as `enqueueFileUpload(_:)`, `refreshOrderHistory(for:)`,
|
|
55
|
+
`validateEmailAddress(_:)`; `handle`, `process`, and `didTap` name neither. Reserve `handle...` for
|
|
56
|
+
reserved for literal UIKit target-action and notification handlers marked `@objc`.
|
|
57
|
+
|
|
58
|
+
Names form grammatical English at the call site: omit the first label when the base
|
|
59
|
+
name and argument form a phrase and included when it clarifies a weak type. Initializer
|
|
60
|
+
arguments that set stored properties use the property names with explicit `self.`, and
|
|
61
|
+
factories use `make...` such as `makeTemplateView(for:)`. Nonmutating methods without side effects
|
|
62
|
+
read as noun phrases, and mutating methods read as imperatives. Pairs follow `sort`/`sorted`,
|
|
63
|
+
`append`/`appending`, `formUnion`/`union`.
|
|
64
|
+
|
|
65
|
+
A delegate method passes the source object first and unlabeled, and never trims its signature because
|
|
66
|
+
one caller currently owns it. Name a source-only `Void` event with the source type plus a tense
|
|
67
|
+
phrase: `draftStore(_:didDeleteDraft:)`. A source-only `Bool` answer uses `can`, `is`, or
|
|
68
|
+
another predicate, never `should` or `may`: `draftStoreCanDeleteDraft`. Name a source-only value with a
|
|
69
|
+
noun phrase with a natural preposition: `numberOfSections(in:)`. Extra arguments make the
|
|
70
|
+
second label describe the event or requested value:
|
|
71
|
+
`messageListDataSource(_:heightForMessageAt:)`.
|
|
72
|
+
|
|
73
|
+
## Protocols and identifiers
|
|
74
|
+
|
|
75
|
+
<!-- level: all -->
|
|
76
|
+
|
|
77
|
+
A protocol carries no `I` prefix and no `Protocol` suffix. Name a domain role with a noun such as `TemplateRepository`. Name a capability for its behavior, often with `-ing`,
|
|
78
|
+
such as `ProgressReporting`, `ThemeColorProviding`, or `LoginCoordinating`. Avoid an automatic `-able`
|
|
79
|
+
name that describes no clear capability. `Provider`, `Repository`, `Client`, and
|
|
80
|
+
`Coordinating` appear only when the protocol serves that role, and no protocol exists per ViewModel or use
|
|
81
|
+
case merely to enable mocks.
|
|
82
|
+
|
|
83
|
+
Presentation identifiers provide stable interfaces for UI identity, diffable data sources,
|
|
84
|
+
navigation, persistence, and tests, named for what they identify rather than the framework
|
|
85
|
+
that consumes them. Snapshot items use stable presentation or domain IDs, never DTO identity,
|
|
86
|
+
array offsets, or index paths. `id` suffices when the enclosing type supplies the context
|
|
87
|
+
such as `MessageRow.id`; a role-qualified name such as `selectedMessageID` or `conversationID`
|
|
88
|
+
serves a scope with several identifiers.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Swift
|
|
2
|
+
|
|
3
|
+
Swift source follows the Apple API Design Guidelines and the local formatting and lint
|
|
4
|
+
configuration. SwiftFormat owns layout. Treat warnings as errors in the build settings.
|
|
5
|
+
|
|
6
|
+
## Files and style
|
|
7
|
+
|
|
8
|
+
Every file imports exactly the top-level modules it uses, as whole modules unless a single
|
|
9
|
+
declaration avoids a known conflict. Group imports by compiler condition, with
|
|
10
|
+
`@testable import` after regular imports in tests. `// MARK: - Section` marks meaningful groups in larger files,
|
|
11
|
+
and a file comment appears only when the file groups several abstractions and the grouping
|
|
12
|
+
needs explanation. Type inference covers a right-hand side with a clear type; an explicit annotation
|
|
13
|
+
covers empty collections, `nil` initial values, weak type information, and public API
|
|
14
|
+
clarity. Enum raw values that map to an external wire or persistence contract need documentation;
|
|
15
|
+
an explicit case list replaces `default` where a future case must force review. Attributes
|
|
16
|
+
with parameters go on their own line when they hurt readability inline.
|
|
17
|
+
|
|
18
|
+
### File organization
|
|
19
|
+
|
|
20
|
+
<!-- level: all -->
|
|
21
|
+
|
|
22
|
+
One primary top-level type per file, with small private helpers allowed beside it. Keep overloads adjacent. Organize extensions rather than scattering them across files. Group SwiftUI
|
|
23
|
+
dynamic properties by property-wrapper type.
|
|
24
|
+
|
|
25
|
+
Good:
|
|
26
|
+
|
|
27
|
+
```swift
|
|
28
|
+
private func sumPositive(_ values: [Int]) -> Int {
|
|
29
|
+
var total = 0
|
|
30
|
+
for value in values where value > 0 {
|
|
31
|
+
total += value
|
|
32
|
+
}
|
|
33
|
+
return total
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Compare the sums of positive values in two collections.
|
|
37
|
+
public func comparePositiveTotals(_ left: [Int], _ right: [Int]) -> Int {
|
|
38
|
+
sumPositive(left) - sumPositive(right)
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Practices
|
|
43
|
+
|
|
44
|
+
Use `let` by default and `var` for required mutation. Class-constrained protocols use
|
|
45
|
+
`AnyObject`. Prefer shorthand types such as `[Element]`, `[Key: Value]`, or `Wrapped?`, and use `Void` for function
|
|
46
|
+
types. Optionals express valid absence rather than sentinel values, compared to
|
|
47
|
+
`nil` when only presence matters and bound when the code uses the value, chained for one-off access.
|
|
48
|
+
|
|
49
|
+
Typed errors distinguish several meaningful failure states. A force unwrap or cast needs a
|
|
50
|
+
proven invariant. At level `all`, use `// swiftlint:disable:next force_unwrapping` with the
|
|
51
|
+
invariant as the reason for an unavoidable unwrap. Avoid global mutable state, with `static let` or computed `static var` over stored
|
|
52
|
+
mutable statics.
|
|
53
|
+
|
|
54
|
+
`guard` handles early exits, `for ... where` a loop guarded by one condition,
|
|
55
|
+
`for` over `forEach` for control flow or async work, and `map`, `compactMap`, and
|
|
56
|
+
`filter` express side-effect-free transformation. Pattern bindings put `let` on each element
|
|
57
|
+
where that avoids ambiguity. Use weak capture only when a callback must not extend the captured
|
|
58
|
+
object's lifetime. Otherwise, capture the immutable values the callback needs. `#fileID` serves production diagnostics; `#filePath`
|
|
59
|
+
only tests and tooling. Overload operators only with their standard meaning, and a
|
|
60
|
+
custom operator exists only as standard domain notation.
|
|
61
|
+
|
|
62
|
+
### API and ownership conventions
|
|
63
|
+
|
|
64
|
+
<!-- level: all -->
|
|
65
|
+
|
|
66
|
+
Structs keep synthesized
|
|
67
|
+
memberwise initializers where sufficient, value types hold data without identity, and classes
|
|
68
|
+
serve identity, reference semantics, lifecycle, observable state, or framework requirements.
|
|
69
|
+
An implicitly unwrapped optional appears only for Apple lifecycle cases such as `@IBOutlet`,
|
|
70
|
+
Objective-C nullability gaps, and test data.
|
|
71
|
+
|
|
72
|
+
Declare top-level access to match the public contract. An extension carries no blanket access
|
|
73
|
+
modifier. Types nest when they only make sense in the parent's context, and a caseless enum
|
|
74
|
+
groups truly related statics. Methods and properties beat free functions unless the function
|
|
75
|
+
follows the standard library and stays symmetric.
|
|
76
|
+
Logging goes through the project logging system, and
|
|
77
|
+
images and colors come from named assets or explicit constructors.
|
|
78
|
+
|
|
79
|
+
## Networking
|
|
80
|
+
|
|
81
|
+
URLs come from `URLComponents` or a typed endpoint value, never concatenated query strings,
|
|
82
|
+
and a production `URL(string:)!` documents its invariant. Inject base URLs and credentials
|
|
83
|
+
at composition. Use `Encodable` types for request bodies with an explicit method and
|
|
84
|
+
content type, never `[String: Any]`. `URLSession` succeeds for non-2xx responses.
|
|
85
|
+
Validate `HTTPURLResponse` status explicitly. Treat a non-HTTP response as a transport
|
|
86
|
+
failure.
|
|
87
|
+
|
|
88
|
+
Responses decode into DTOs mapped into domain values at the boundary. Validate required fields and
|
|
89
|
+
external enum values there. Do not invent a fallback ID, date, or case.
|
|
90
|
+
Clients expose typed errors distinguishing encoding, decoding, transport, invalid response,
|
|
91
|
+
invalid status, and cancellation, which stays distinguishable when navigation supersedes a
|
|
92
|
+
task. When you write tests, inject the transport and cover request construction, status validation,
|
|
93
|
+
decoding failure, cancellation, and mapping without a live service.
|
|
94
|
+
|
|
95
|
+
### Networking ownership
|
|
96
|
+
|
|
97
|
+
<!-- level: all -->
|
|
98
|
+
|
|
99
|
+
A network client owns base URL, path construction, query items, method, headers, body
|
|
100
|
+
encoding, transport calls, status validation, and decoding. Views and view models never build
|
|
101
|
+
a `URLRequest`, call `URLSession`, decode a DTO, or inspect a status. Prefer Foundation `URLSession`
|
|
102
|
+
with async/await rather than a third-party networking framework.
|
|
103
|
+
|
|
104
|
+
## Documentation comments
|
|
105
|
+
|
|
106
|
+
`///` documents a declaration before its attributes and modifiers. Swift markup uses
|
|
107
|
+
`- Parameter name:`, `- Parameters:`, `- Returns`, and `- Throws`. Backticks name parameters
|
|
108
|
+
and types. `// MARK:`, implementation notes, and tool directives use ordinary `//` comments.
|
|
109
|
+
|
|
110
|
+
## Concurrency and errors
|
|
111
|
+
|
|
112
|
+
Structured `async`/`await` with `async throws` APIs replaces callback pyramids and unmanaged
|
|
113
|
+
task trees. UI state mutates on the main actor. When the project uses view models, keep them
|
|
114
|
+
`@MainActor`.
|
|
115
|
+
|
|
116
|
+
Expensive work moves into use cases, repositories, actors, or background tasks. `Task` creation stays
|
|
117
|
+
in the modules that need them: view models, coordinators, or views through `.task`, never in an initializer, always
|
|
118
|
+
with an owner and a cancellation strategy. Store handles when a disappearing view, a
|
|
119
|
+
retry, or a newer request must cancel older work. Check cancellation in long loops and
|
|
120
|
+
before publishing stale results.
|
|
121
|
+
|
|
122
|
+
Shared mutable state sits behind actors, main-actor
|
|
123
|
+
isolation, or explicit synchronization, with actor isolation preferred over
|
|
124
|
+
`DispatchQueue.main.async`. The compiler verifies `Sendable` designs; a module without
|
|
125
|
+
concurrency annotations gets `@preconcurrency import`, and `@unchecked Sendable` or
|
|
126
|
+
`nonisolated(unsafe)` appears only with a documented local invariant and no safer design.
|
|
127
|
+
Inject clocks and scheduling interfaces where time affects logic or tests.
|
|
128
|
+
|
|
129
|
+
Typed errors serve callers with different recovery paths, and untyped `Error` only generic
|
|
130
|
+
boundaries where cases add nothing. Technical errors become user-facing messages at the presentation
|
|
131
|
+
boundary. Failed operations throw, and UI loading and error display use explicit state enums.
|
|
132
|
+
Reserve `fatalError` for an unrecoverable programmer error with a documented invariant,
|
|
133
|
+
`assertionFailure` for an unexpected but recoverable state, and `precondition` for an invariant
|
|
134
|
+
that continuing requires.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# SwiftUI
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
<!-- level: all -->
|
|
6
|
+
|
|
7
|
+
This rule picks no presentation pattern, such as Model-View-ViewModel (MVVM) or The Composable Architecture (TCA); a project that wants one writes it
|
|
8
|
+
down.
|
|
9
|
+
|
|
10
|
+
## SwiftUI views
|
|
11
|
+
|
|
12
|
+
- Keep view-local state in the view when its lifetime matches the view's identity.
|
|
13
|
+
Use `@State`, `@FocusState`, `@GestureState`, and bindings according to their
|
|
14
|
+
ownership and read contracts.
|
|
15
|
+
- Keep view-owned state properties private. Inject values owned by a parent
|
|
16
|
+
through the appropriate input or binding.
|
|
17
|
+
- Preserve read and actor isolation when state changes.
|
|
18
|
+
- Do not store derived state in `@State` when source state provides enough data to compute it.
|
|
19
|
+
- Avoid heavy computation in `body`; SwiftUI can evaluate it repeatedly.
|
|
20
|
+
- Preserve cancellation and stale-result handling for lifecycle work started with
|
|
21
|
+
`.task`, `.onAppear`, or refresh actions.
|
|
22
|
+
|
|
23
|
+
### View organization
|
|
24
|
+
|
|
25
|
+
<!-- level: all -->
|
|
26
|
+
|
|
27
|
+
- Views render state and forward user intent to the project's operation or state owner.
|
|
28
|
+
- Keep business workflows, network calls, database calls, and concrete infrastructure
|
|
29
|
+
construction in their existing owners.
|
|
30
|
+
- Do not add explicit `@ViewBuilder` to `body`; the compiler provides it.
|
|
31
|
+
- Use `@ViewBuilder` for helper properties or functions that need conditional view branches.
|
|
32
|
+
- Omit `else { EmptyView() }` when the missing branch already produces no content.
|
|
33
|
+
- Extract subviews by visual responsibility, not to reduce line count.
|
|
34
|
+
- Give reusable views explicit inputs instead of hidden dependencies on broad environment objects.
|
|
35
|
+
- Keep short display formatting local. Put reusable or domain-sensitive formatting
|
|
36
|
+
with the behavior that owns it.
|
|
37
|
+
|
|
38
|
+
## State ownership
|
|
39
|
+
|
|
40
|
+
<!-- level: all -->
|
|
41
|
+
|
|
42
|
+
Keep screen and flow state in the project's selected state owner. Use a shared owner
|
|
43
|
+
when multiple screens need the same state. A store owns cohesive feature or domain
|
|
44
|
+
state; app-wide state belongs in app composition only for global state, such as
|
|
45
|
+
session, theme, or connectivity.
|
|
46
|
+
|
|
47
|
+
Prefer an enum for mutually exclusive loading states instead of parallel booleans.
|
|
48
|
+
Keep transient UI state out of the domain model.
|
|
49
|
+
|
|
50
|
+
## Shared state correctness
|
|
51
|
+
|
|
52
|
+
Use a single source of truth or an explicit refresh strategy when multiple views
|
|
53
|
+
consume the same domain state. Do not assume that creating a second observable
|
|
54
|
+
object keeps it synchronized with the first. Preserve the intended object's lifetime
|
|
55
|
+
across view reconstruction and identity changes.
|
|
56
|
+
|
|
57
|
+
## Accessibility
|
|
58
|
+
|
|
59
|
+
- Interactive controls get traits, states, and hints when the visible label does not provide enough information.
|
|
60
|
+
- `.accessibilityHidden(true)` hides only decorative or duplicate content.
|
|
61
|
+
- Ask the user to check navigation, modal presentation, focus, and custom controls with VoiceOver.
|