@zigrivers/scaffold 3.6.0 → 3.8.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 (115) hide show
  1. package/README.md +127 -12
  2. package/content/knowledge/backend/backend-api-design.md +103 -0
  3. package/content/knowledge/backend/backend-architecture.md +100 -0
  4. package/content/knowledge/backend/backend-async-patterns.md +101 -0
  5. package/content/knowledge/backend/backend-auth-patterns.md +100 -0
  6. package/content/knowledge/backend/backend-conventions.md +105 -0
  7. package/content/knowledge/backend/backend-data-modeling.md +102 -0
  8. package/content/knowledge/backend/backend-deployment.md +100 -0
  9. package/content/knowledge/backend/backend-dev-environment.md +102 -0
  10. package/content/knowledge/backend/backend-observability.md +102 -0
  11. package/content/knowledge/backend/backend-project-structure.md +100 -0
  12. package/content/knowledge/backend/backend-requirements.md +103 -0
  13. package/content/knowledge/backend/backend-security.md +104 -0
  14. package/content/knowledge/backend/backend-testing.md +101 -0
  15. package/content/knowledge/backend/backend-worker-patterns.md +100 -0
  16. package/content/knowledge/cli/cli-architecture.md +101 -0
  17. package/content/knowledge/cli/cli-conventions.md +117 -0
  18. package/content/knowledge/cli/cli-dev-environment.md +121 -0
  19. package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
  20. package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
  21. package/content/knowledge/cli/cli-output-patterns.md +107 -0
  22. package/content/knowledge/cli/cli-project-structure.md +124 -0
  23. package/content/knowledge/cli/cli-requirements.md +101 -0
  24. package/content/knowledge/cli/cli-shell-integration.md +130 -0
  25. package/content/knowledge/cli/cli-testing.md +134 -0
  26. package/content/knowledge/library/library-api-design.md +306 -0
  27. package/content/knowledge/library/library-architecture.md +247 -0
  28. package/content/knowledge/library/library-bundling.md +244 -0
  29. package/content/knowledge/library/library-conventions.md +229 -0
  30. package/content/knowledge/library/library-dev-environment.md +220 -0
  31. package/content/knowledge/library/library-documentation.md +300 -0
  32. package/content/knowledge/library/library-project-structure.md +237 -0
  33. package/content/knowledge/library/library-requirements.md +173 -0
  34. package/content/knowledge/library/library-security.md +257 -0
  35. package/content/knowledge/library/library-testing.md +319 -0
  36. package/content/knowledge/library/library-type-definitions.md +284 -0
  37. package/content/knowledge/library/library-versioning.md +300 -0
  38. package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
  39. package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
  40. package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
  41. package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
  42. package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
  43. package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
  44. package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
  45. package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
  46. package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
  47. package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
  48. package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
  49. package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
  50. package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
  51. package/content/knowledge/web-app/web-app-architecture.md +116 -0
  52. package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
  53. package/content/knowledge/web-app/web-app-conventions.md +121 -0
  54. package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
  55. package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
  56. package/content/knowledge/web-app/web-app-deployment.md +134 -0
  57. package/content/knowledge/web-app/web-app-design-system.md +158 -0
  58. package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
  59. package/content/knowledge/web-app/web-app-observability.md +221 -0
  60. package/content/knowledge/web-app/web-app-project-structure.md +160 -0
  61. package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
  62. package/content/knowledge/web-app/web-app-requirements.md +112 -0
  63. package/content/knowledge/web-app/web-app-security.md +193 -0
  64. package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
  65. package/content/knowledge/web-app/web-app-testing.md +249 -0
  66. package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
  67. package/content/methodology/backend-overlay.yml +73 -0
  68. package/content/methodology/cli-overlay.yml +69 -0
  69. package/content/methodology/library-overlay.yml +67 -0
  70. package/content/methodology/mobile-app-overlay.yml +71 -0
  71. package/content/methodology/web-app-overlay.yml +79 -0
  72. package/dist/cli/commands/init.d.ts +21 -0
  73. package/dist/cli/commands/init.d.ts.map +1 -1
  74. package/dist/cli/commands/init.js +261 -13
  75. package/dist/cli/commands/init.js.map +1 -1
  76. package/dist/cli/commands/init.test.js +206 -0
  77. package/dist/cli/commands/init.test.js.map +1 -1
  78. package/dist/config/schema.d.ts +1392 -64
  79. package/dist/config/schema.d.ts.map +1 -1
  80. package/dist/config/schema.js +82 -5
  81. package/dist/config/schema.js.map +1 -1
  82. package/dist/config/schema.test.js +302 -1
  83. package/dist/config/schema.test.js.map +1 -1
  84. package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
  85. package/dist/core/assembly/overlay-loader.js +2 -1
  86. package/dist/core/assembly/overlay-loader.js.map +1 -1
  87. package/dist/core/assembly/overlay-loader.test.js +56 -0
  88. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  89. package/dist/e2e/game-pipeline.test.js +1 -0
  90. package/dist/e2e/game-pipeline.test.js.map +1 -1
  91. package/dist/e2e/project-type-overlays.test.d.ts +16 -0
  92. package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
  93. package/dist/e2e/project-type-overlays.test.js +834 -0
  94. package/dist/e2e/project-type-overlays.test.js.map +1 -0
  95. package/dist/types/config.d.ts +19 -2
  96. package/dist/types/config.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +0 -1
  98. package/dist/types/index.d.ts.map +1 -1
  99. package/dist/types/index.js +0 -1
  100. package/dist/types/index.js.map +1 -1
  101. package/dist/wizard/questions.d.ts +27 -1
  102. package/dist/wizard/questions.d.ts.map +1 -1
  103. package/dist/wizard/questions.js +142 -3
  104. package/dist/wizard/questions.js.map +1 -1
  105. package/dist/wizard/questions.test.js +206 -8
  106. package/dist/wizard/questions.test.js.map +1 -1
  107. package/dist/wizard/wizard.d.ts +21 -0
  108. package/dist/wizard/wizard.d.ts.map +1 -1
  109. package/dist/wizard/wizard.js +27 -1
  110. package/dist/wizard/wizard.js.map +1 -1
  111. package/package.json +1 -1
  112. package/dist/types/wizard.d.ts +0 -14
  113. package/dist/types/wizard.d.ts.map +0 -1
  114. package/dist/types/wizard.js +0 -2
  115. package/dist/types/wizard.js.map +0 -1
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: cli-shell-integration
3
+ description: Shell completion generation for bash/zsh/fish, man page generation, dotfile conventions, PATH management, and shell aliases
4
+ topics: [cli, shell-integration, completion, man-pages, dotfiles, path-management, aliases]
5
+ ---
6
+
7
+ Shell integration is the difference between a CLI that feels native and one that feels like a foreign object. Completion, man pages, and dotfile patterns are not optional polish — they are the features that determine whether power users adopt the tool or abandon it for something that respects their shell workflow.
8
+
9
+ ## Summary
10
+
11
+ Shell integration — completion scripts, man pages, dotfile modifications, and PATH management — determines whether power users adopt the tool or abandon it. Generate completion scripts from the CLI definition for bash, zsh, and fish. Provide an `install-completions` subcommand. When modifying shell startup files, use clearly marked comment blocks and always provide an uninstall command.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Shell Completion Generation
16
+
17
+ Provide completion scripts for bash, zsh, and fish. Generate them from the CLI definition rather than hand-writing them — they will stay in sync as commands and flags evolve:
18
+
19
+ **Node.js (yargs)**
20
+ ```bash
21
+ my-cli completion # Outputs bash/zsh completion script
22
+ my-cli completion >> ~/.bashrc
23
+ ```
24
+
25
+ **Cobra (Go)**
26
+ ```bash
27
+ my-cli completion bash # bash
28
+ my-cli completion zsh # zsh
29
+ my-cli completion fish # fish
30
+ my-cli completion powershell
31
+ ```
32
+
33
+ **clap (Rust)** with `clap_complete`:
34
+ ```rust
35
+ // Generate at build time and install to /usr/local/share/bash-completion/completions/
36
+ generate(Shell::Bash, &mut cmd, "my-cli", &mut io::stdout());
37
+ ```
38
+
39
+ **click (Python)**: Use `click-completion` or the built-in `shell_complete` parameter.
40
+
41
+ Completion installation patterns:
42
+ - **bash**: Source from `~/.bashrc` or drop in `/etc/bash_completion.d/` or `~/.bash_completion.d/`
43
+ - **zsh**: Drop in `$fpath` directory, e.g., `/usr/local/share/zsh/site-functions/_my-cli`
44
+ - **fish**: Drop in `~/.config/fish/completions/my-cli.fish`
45
+
46
+ Provide a `my-cli install-completions` subcommand that writes the correct file for the detected shell. Always print the path written and what the user must do to activate it (e.g., restart shell or run `source ~/.bashrc`).
47
+
48
+ ### Man Page Generation
49
+
50
+ Man pages are the canonical reference documentation. Generate them from the CLI definition:
51
+
52
+ **Go (cobra)**: `cobra-man` generates man pages from Cobra commands.
53
+
54
+ **Node.js**: `marked-man` converts Markdown to man format. `ronn` (Ruby) converts richly formatted Markdown to man.
55
+
56
+ **Rust (clap)**: `clap_mangen` generates man pages from clap definitions.
57
+
58
+ Man page installation:
59
+ - System-wide: `/usr/local/share/man/man1/my-cli.1`
60
+ - User-local: `~/.local/share/man/man1/my-cli.1`
61
+ - Run `mandb` (Linux) or ensure `$MANPATH` includes the directory
62
+
63
+ Homebrew formulae automatically install man pages if placed in `share/man/man1/`. For npm packages, include a `man` field in `package.json`.
64
+
65
+ ### Dotfile Conventions
66
+
67
+ When the CLI modifies shell startup files (`~/.bashrc`, `~/.zshrc`), follow these rules:
68
+
69
+ - Never overwrite startup files — append only
70
+ - Use clearly marked comment blocks:
71
+ ```bash
72
+ # >>> my-cli init >>>
73
+ export PATH="$HOME/.my-cli/bin:$PATH"
74
+ eval "$(my-cli shell-init)"
75
+ # <<< my-cli init <<<
76
+ ```
77
+ - The markers enable the tool to detect existing installation and idempotently update the block
78
+ - Always provide `my-cli uninstall` or `my-cli shell-remove` that removes exactly these markers
79
+ - Never add content outside the marked block
80
+
81
+ On first install, detect which shell config files exist and are writable: check `$SHELL`, then look for `~/.zshrc`, `~/.bashrc`, `~/.bash_profile` in that order.
82
+
83
+ ### PATH Management
84
+
85
+ When the tool installs binaries or shims to a user-local directory:
86
+
87
+ ```bash
88
+ # Typical user-local bin directory
89
+ ~/.local/bin/ # XDG standard (Linux/macOS)
90
+ ~/.my-cli/bin/ # Tool-specific (when multiple versions coexist)
91
+ ```
92
+
93
+ Check if the directory is already on `$PATH` before suggesting the user add it. If not on `$PATH`, print the exact line to add and which file to add it to:
94
+
95
+ ```
96
+ Add this to ~/.zshrc:
97
+ export PATH="$HOME/.local/bin:$PATH"
98
+ ```
99
+
100
+ Do not silently modify PATH for the current process and assume it persists — shell environment changes only persist through startup file modification or explicit user action.
101
+
102
+ ### Shell Aliases
103
+
104
+ Provide a mechanism to generate useful shell aliases for common command combinations:
105
+
106
+ ```bash
107
+ # my-cli generates aliases for common workflows
108
+ my-cli alias generate >> ~/.zshrc
109
+ ```
110
+
111
+ Aliases should be documented and user-editable. Never require aliases for core functionality — they are convenience, not architecture.
112
+
113
+ ### Shell Detection
114
+
115
+ Detect the user's shell reliably:
116
+
117
+ ```bash
118
+ # Most reliable: check $SHELL environment variable
119
+ SHELL_NAME=$(basename "$SHELL")
120
+
121
+ # Fallback: check running process
122
+ ps -p $PPID -o comm= | sed 's/^-//'
123
+ ```
124
+
125
+ Map shell name to config file:
126
+ - `zsh` → `~/.zshrc`
127
+ - `bash` → `~/.bashrc` (Linux) or `~/.bash_profile` (macOS interactive login shells)
128
+ - `fish` → `~/.config/fish/config.fish`
129
+
130
+ When the shell cannot be detected, print instructions for all three common shells and let the user pick. Never guess and silently write to the wrong file.
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: cli-testing
3
+ description: CLI integration testing by spawning processes, snapshot testing help text, mock filesystem, environment variable testing, and CI matrix testing
4
+ topics: [cli, testing, integration-tests, snapshot-testing, mock-filesystem, ci-matrix, exit-codes]
5
+ ---
6
+
7
+ CLI testing requires a different mindset than library testing. The contract being tested is behavioral: given this argv and environment, what does the tool write to stdout, what does it write to stderr, and what exit code does it return? Unit tests for business logic are necessary but not sufficient — integration tests that spawn the actual binary catch the class of bugs that only appear at the boundary between argument parsing and execution.
8
+
9
+ ## Summary
10
+
11
+ CLI testing requires spawning the actual binary and asserting on stdout, stderr, and exit code. Snapshot test help text to catch accidental regressions. Isolate filesystem tests with temporary directories and mock `$HOME`/`XDG_CONFIG_HOME`. Test across OS/runtime matrices in CI including Windows.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Integration Testing by Spawning the Process
16
+
17
+ The most valuable CLI test spawns the actual binary and asserts on stdout, stderr, and exit code:
18
+
19
+ **Node.js (vitest or jest)**
20
+ ```typescript
21
+ import { execSync } from 'child_process';
22
+
23
+ test('build succeeds with valid input', () => {
24
+ const result = execSync('node bin/my-cli build --input fixture.txt', {
25
+ encoding: 'utf8',
26
+ env: { ...process.env, XDG_CONFIG_HOME: tmpDir }
27
+ });
28
+ expect(result).toContain('Build complete');
29
+ });
30
+
31
+ test('exits 2 on unknown flag', () => {
32
+ expect(() =>
33
+ execSync('node bin/my-cli --unknown-flag', { encoding: 'utf8', stdio: 'pipe' })
34
+ ).toThrow('exit code 2');
35
+ });
36
+ ```
37
+
38
+ **Rust**
39
+ ```rust
40
+ use assert_cmd::Command;
41
+
42
+ #[test]
43
+ fn build_succeeds() {
44
+ Command::cargo_bin("my-cli")
45
+ .unwrap()
46
+ .arg("build")
47
+ .assert()
48
+ .success()
49
+ .stdout(predicates::str::contains("Build complete"));
50
+ }
51
+ ```
52
+
53
+ **Bats (shell)**
54
+ ```bash
55
+ @test "exits 2 on missing required argument" {
56
+ run my-cli deploy
57
+ [ "$status" -eq 2 ]
58
+ [[ "$output" =~ "required" ]]
59
+ }
60
+ ```
61
+
62
+ Always test exit codes. Always test that error output goes to stderr. Always test the success path and the most common failure paths.
63
+
64
+ ### Snapshot Testing for Help Text
65
+
66
+ Help text is part of the public API — changes should be intentional. Snapshot tests catch accidental regressions:
67
+
68
+ ```typescript
69
+ test('help text matches snapshot', () => {
70
+ const { stdout } = execSync('node bin/my-cli --help', { encoding: 'utf8' });
71
+ expect(stdout).toMatchSnapshot();
72
+ });
73
+ ```
74
+
75
+ Update snapshots intentionally when help text changes. In CI, fail on unexpected snapshot drift. This also catches typos and formatting issues in help output.
76
+
77
+ ### Mock Filesystem
78
+
79
+ Tests that interact with the filesystem must be isolated. Use temporary directories or a mock filesystem:
80
+
81
+ **Node.js**
82
+ ```typescript
83
+ import { mkdtempSync, rmSync } from 'fs';
84
+ import { tmpdir } from 'os';
85
+
86
+ let tmpDir: string;
87
+ beforeEach(() => { tmpDir = mkdtempSync(tmpdir() + '/my-cli-test-'); });
88
+ afterEach(() => { rmSync(tmpDir, { recursive: true }); });
89
+ ```
90
+
91
+ For unit tests that don't need real disk I/O, `memfs` provides an in-memory filesystem that satisfies the Node.js `fs` module interface.
92
+
93
+ **Rust**: Use `tempfile::TempDir`. Tests run in parallel by default — each test gets its own temp directory to avoid interference.
94
+
95
+ **Key rule**: Never write to `$HOME`, `~/.config`, or any real user directory in tests. Set `HOME` and `XDG_CONFIG_HOME` to the temp directory.
96
+
97
+ ### Environment Variable Testing
98
+
99
+ Test behavior driven by environment variables:
100
+
101
+ ```typescript
102
+ test('respects NO_COLOR', () => {
103
+ const result = execSync('node bin/my-cli status', {
104
+ encoding: 'utf8',
105
+ env: { ...process.env, NO_COLOR: '1' }
106
+ });
107
+ // Assert no ANSI escape sequences in output
108
+ expect(result).not.toMatch(/\x1b\[/);
109
+ });
110
+ ```
111
+
112
+ Test the full env var precedence chain: flag overrides env var, env var overrides config file. Each level should be independently testable.
113
+
114
+ ### CI Matrix Testing
115
+
116
+ Test across multiple operating systems and runtime versions:
117
+
118
+ ```yaml
119
+ # GitHub Actions
120
+ strategy:
121
+ matrix:
122
+ os: [ubuntu-latest, macos-latest, windows-latest]
123
+ node: ['18', '20', '22']
124
+ ```
125
+
126
+ Windows-specific concerns: path separators (`\` vs `/`), line endings (`\r\n` vs `\n`), `%APPDATA%` vs `$HOME/.config`, and `PATHEXT` for binary extension handling. Test on Windows even if you do not primarily develop on it.
127
+
128
+ ### Test Pyramid for CLIs
129
+
130
+ - **Unit tests (fast)**: Business logic in `utils/`, pure functions, config parsing, output formatters
131
+ - **Integration tests (medium)**: Spawn the CLI process, assert stdout/stderr/exit code for key scenarios
132
+ - **End-to-end tests (slow, optional)**: Full workflow against real external services — run in CI on schedule, not on every PR
133
+
134
+ Keep integration tests fast by using fixtures (pre-created files) rather than generating test input dynamically. A full integration test suite should complete in under 30 seconds.
@@ -0,0 +1,306 @@
1
+ ---
2
+ name: library-api-design
3
+ description: Public surface design, method signatures, error contracts, and extension points for published library APIs
4
+ topics: [library, api-design, public-surface, method-signatures, error-contracts, extension-points]
5
+ ---
6
+
7
+ Library API design is the highest-leverage activity in library development. A well-designed API makes correct usage easy and incorrect usage hard, survives multiple major versions without fundamental restructuring, and communicates intent through its shape alone. Poor API design cannot be fixed without breaking changes — every naming mistake, parameter order error, and missing overload becomes permanent once consumers adopt it. Design APIs from the consumer's perspective first, implementation second.
8
+
9
+ ## Summary
10
+
11
+ Design APIs by writing consumer call sites before writing implementation. Prefer named options objects over positional parameters beyond two arguments. Return values should be typed as specifically as possible — avoid returning `any` or overly wide union types. Error contracts must be explicit: document what each function throws, when, and why. Provide extension points through composition (options injection, middleware, plugins) rather than inheritance. Make the happy path obvious and the error path impossible to ignore.
12
+
13
+ Core principles:
14
+ - Pit-of-success design: the obvious way to use the API is the correct way
15
+ - Named options objects for 3+ parameters
16
+ - Explicit error contracts (typed throws, documented in JSDoc)
17
+ - Overloads for genuinely different call signatures
18
+ - Consistent return type patterns (never `T | undefined` when you can overload)
19
+
20
+ ## Deep Guidance
21
+
22
+ ### Write Call Sites First
23
+
24
+ Before implementing any function, write the code that will call it:
25
+
26
+ ```typescript
27
+ // Step 1: Write how consumers will use this
28
+ import { parseConfig } from 'my-library'
29
+
30
+ // Use case 1: parse a string, get typed config
31
+ const config = parseConfig(rawString)
32
+
33
+ // Use case 2: parse with strict mode
34
+ const config = parseConfig(rawString, { strict: true })
35
+
36
+ // Use case 3: parse a file path (different input)
37
+ const config = await parseConfigFile('./config.toml')
38
+
39
+ // Use case 4: handle parse errors gracefully
40
+ try {
41
+ const config = parseConfig(rawString)
42
+ } catch (err) {
43
+ if (err instanceof ParseError) {
44
+ console.error(`Parse failed at line ${err.line}: ${err.message}`)
45
+ }
46
+ }
47
+
48
+ // Step 2: Now design the API to make this work naturally
49
+ export function parseConfig(input: string, options?: ParseOptions): Config
50
+ export async function parseConfigFile(path: string, options?: ParseOptions): Promise<Config>
51
+ ```
52
+
53
+ This technique reveals usability issues before any code is written.
54
+
55
+ ### Options Object Pattern
56
+
57
+ Positional parameters beyond two create cognitive load and fragile call sites:
58
+
59
+ ```typescript
60
+ // BAD: positional parameters — order is arbitrary, easy to mix up
61
+ function connect(host: string, port: number, timeout: number, ssl: boolean, retries: number): Client
62
+
63
+ // Called as: connect('localhost', 5432, 30000, true, 3)
64
+ // Which is timeout and which is retries? Must check signature every time.
65
+
66
+ // GOOD: named options object
67
+ interface ConnectOptions {
68
+ host: string
69
+ port: number
70
+ timeout?: number // ms, default: 30000
71
+ ssl?: boolean // default: false
72
+ retries?: number // default: 3
73
+ }
74
+
75
+ function connect(options: ConnectOptions): Client
76
+ // connect({ host: 'localhost', port: 5432, ssl: true })
77
+ // Self-documenting call site. New options add without breaking callers.
78
+ ```
79
+
80
+ **Options object rules:**
81
+ - All options beyond the first two required parameters go in an options object
82
+ - Options should have sensible defaults (document the defaults in JSDoc)
83
+ - Required options stay required; don't make everything optional
84
+ - Never use boolean flags that change behavior fundamentally — use discriminated unions
85
+
86
+ ```typescript
87
+ // BAD: boolean flag that means completely different behavior
88
+ function parse(input: string, isFile: boolean): Config
89
+
90
+ // GOOD: separate functions or discriminated union
91
+ function parseString(input: string): Config
92
+ function parseFile(path: string): Promise<Config>
93
+ // Or:
94
+ type ParseInput = { type: 'string'; value: string } | { type: 'file'; path: string }
95
+ function parse(input: ParseInput): Config | Promise<Config>
96
+ ```
97
+
98
+ ### Method Signature Design
99
+
100
+ **Overloads for genuinely different signatures:**
101
+ ```typescript
102
+ // Overloads allow TypeScript to narrow the return type based on input
103
+ function parse(input: string): Config
104
+ function parse(input: string, options: { async: true }): Promise<Config>
105
+ function parse(input: string, options: { async: false }): Config
106
+ function parse(input: string, options?: ParseOptions): Config | Promise<Config> {
107
+ // implementation
108
+ }
109
+ ```
110
+
111
+ Use overloads sparingly. Each overload is a commitment to maintain that signature. If you find yourself needing many overloads, reconsider whether the API should be split into separate functions.
112
+
113
+ **Generic constraints — add them when they add value:**
114
+ ```typescript
115
+ // Good: generic constraint enables type narrowing
116
+ function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>
117
+
118
+ // Bad: generic without constraint is less type-safe
119
+ function pick<T, K>(obj: T, keys: K[]): any
120
+
121
+ // Bad: generic where it adds no value (always the same type)
122
+ function identity<T>(value: T): T // Fine for teaching, rarely needed in practice
123
+ ```
124
+
125
+ **Return type specificity:**
126
+ ```typescript
127
+ // BAD: too wide
128
+ function getUser(id: string): object
129
+
130
+ // BAD: any
131
+ function parseYAML(input: string): any
132
+
133
+ // GOOD: specific types
134
+ function getUser(id: string): User | null // null when not found
135
+ function parseYAML<T = unknown>(input: string): T // generic with default
136
+
137
+ // BEST when the shape is known:
138
+ interface User {
139
+ id: string
140
+ name: string
141
+ email: string
142
+ createdAt: Date
143
+ }
144
+ function getUser(id: string): User | null
145
+ ```
146
+
147
+ ### Error Contracts
148
+
149
+ Every public function must have a documented error contract. Document errors in JSDoc and in a separate errors section of the API documentation.
150
+
151
+ **JSDoc error documentation:**
152
+ ```typescript
153
+ /**
154
+ * Parse a configuration string into a typed Config object.
155
+ *
156
+ * @param input - TOML-formatted configuration string
157
+ * @param options - Parsing options
158
+ * @returns Parsed and validated Config object
159
+ *
160
+ * @throws {ParseError} If the input string is not valid TOML.
161
+ * `ParseError.line` and `ParseError.column` indicate the error location.
162
+ * @throws {ValidationError} If the parsed config fails schema validation.
163
+ * `ValidationError.errors` contains the list of validation failures.
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * try {
168
+ * const config = parseConfig('[server]\nhost = "localhost"')
169
+ * } catch (err) {
170
+ * if (err instanceof ParseError) {
171
+ * console.error(`Syntax error at line ${err.line}`)
172
+ * }
173
+ * }
174
+ * ```
175
+ */
176
+ export function parseConfig(input: string, options?: ParseOptions): Config
177
+ ```
178
+
179
+ **Result type pattern (alternative to throws):**
180
+ For APIs where errors are expected and should be handled inline, a Result type is cleaner than throws:
181
+
182
+ ```typescript
183
+ export type Result<T, E extends Error = Error> =
184
+ | { ok: true; value: T }
185
+ | { ok: false; error: E }
186
+
187
+ export function tryParseConfig(input: string): Result<Config, ParseError | ValidationError> {
188
+ try {
189
+ return { ok: true, value: parseConfig(input) }
190
+ } catch (err) {
191
+ return { ok: false, error: err as ParseError | ValidationError }
192
+ }
193
+ }
194
+
195
+ // Consumer usage:
196
+ const result = tryParseConfig(input)
197
+ if (result.ok) {
198
+ console.log(result.value.server.host)
199
+ } else {
200
+ console.error(result.error.message)
201
+ }
202
+ ```
203
+
204
+ Provide both patterns when the use case warrants: throwing for "should not fail" paths, Result type for "expected to sometimes fail" paths.
205
+
206
+ ### Extension Points
207
+
208
+ Design extension points that don't require forking or subclassing:
209
+
210
+ **Middleware pattern for transform pipelines:**
211
+ ```typescript
212
+ export interface ParseMiddleware {
213
+ (input: string, next: (input: string) => Config): Config
214
+ }
215
+
216
+ export function createParser(middlewares: ParseMiddleware[] = []): Parser {
217
+ return {
218
+ parse(input: string): Config {
219
+ const chain = middlewares.reduceRight(
220
+ (next: (i: string) => Config, mw) => (i: string) => mw(i, next),
221
+ parseRaw
222
+ )
223
+ return chain(input)
224
+ }
225
+ }
226
+ }
227
+
228
+ // Consumer adds preprocessing:
229
+ const parser = createParser([
230
+ (input, next) => next(input.trim().toLowerCase()),
231
+ (input, next) => {
232
+ const result = next(input)
233
+ return { ...result, source: 'custom' }
234
+ }
235
+ ])
236
+ ```
237
+
238
+ **Hook pattern for lifecycle events:**
239
+ ```typescript
240
+ export interface ClientHooks {
241
+ beforeRequest?: (req: Request) => Request | Promise<Request>
242
+ afterResponse?: (res: Response) => Response | Promise<Response>
243
+ onError?: (err: Error) => void
244
+ }
245
+
246
+ export function createClient(options: ClientOptions & { hooks?: ClientHooks }): Client
247
+ ```
248
+
249
+ **Avoid class inheritance as extension:**
250
+ ```typescript
251
+ // BAD: forces consumers to subclass
252
+ class BaseClient {
253
+ protected abstract buildRequest(options: RequestOptions): Request
254
+ // Consumers must extend to customize
255
+ }
256
+
257
+ // GOOD: inject the behavior
258
+ type RequestBuilder = (options: RequestOptions) => Request
259
+
260
+ function createClient(options: {
261
+ buildRequest?: RequestBuilder
262
+ }): Client
263
+ ```
264
+
265
+ Subclassing creates tight coupling between the consumer and the library's internal class hierarchy. Every internal restructuring becomes a breaking change.
266
+
267
+ ### Fluent API Design
268
+
269
+ Fluent APIs (method chaining) improve readability for configuration-heavy builders:
270
+
271
+ ```typescript
272
+ // Query builder example
273
+ export class QueryBuilder<T> {
274
+ private _where: WhereClause[] = []
275
+ private _orderBy: OrderClause[] = []
276
+ private _limit?: number
277
+
278
+ where(field: keyof T, op: Operator, value: unknown): this {
279
+ this._where.push({ field: field as string, op, value })
280
+ return this
281
+ }
282
+
283
+ orderBy(field: keyof T, direction: 'asc' | 'desc' = 'asc'): this {
284
+ this._orderBy.push({ field: field as string, direction })
285
+ return this
286
+ }
287
+
288
+ limit(n: number): this {
289
+ this._limit = n
290
+ return this
291
+ }
292
+
293
+ build(): Query<T> {
294
+ return { where: this._where, orderBy: this._orderBy, limit: this._limit }
295
+ }
296
+ }
297
+
298
+ // Consumer:
299
+ const query = new QueryBuilder<User>()
300
+ .where('active', '=', true)
301
+ .orderBy('createdAt', 'desc')
302
+ .limit(10)
303
+ .build()
304
+ ```
305
+
306
+ Use fluent APIs for builders and configuration DSLs. Avoid them for operational functions — `parseConfig(input).validate().execute()` is harder to debug than three explicit function calls.