@rawsql-ts/ztd-cli 0.16.0 → 0.17.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.
- package/README.md +33 -20
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +372 -127
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +4 -4
- package/dist/commands/lint.js +60 -40
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/ztdConfig.d.ts +2 -2
- package/dist/commands/ztdConfig.js +26 -12
- package/dist/commands/ztdConfig.js.map +1 -1
- package/dist/utils/optionalDependencies.d.ts +35 -0
- package/dist/utils/optionalDependencies.js +96 -0
- package/dist/utils/optionalDependencies.js.map +1 -0
- package/package.json +16 -9
- package/templates/AGENTS.md +36 -309
- package/templates/README.md +12 -215
- package/templates/dist/ztd-cli/templates/src/db/sql-client.ts +24 -0
- package/templates/src/AGENTS.md +26 -0
- package/templates/src/catalog/AGENTS.md +37 -0
- package/templates/src/catalog/runtime/AGENTS.md +75 -0
- package/templates/src/catalog/runtime/_coercions.ts +1 -0
- package/templates/src/catalog/runtime/_smoke.runtime.ts +21 -0
- package/templates/src/catalog/specs/AGENTS.md +48 -0
- package/templates/src/catalog/specs/_smoke.spec.arktype.ts +21 -0
- package/templates/src/catalog/specs/_smoke.spec.zod.ts +20 -0
- package/templates/src/db/sql-client.ts +5 -5
- package/templates/src/jobs/AGENTS.md +26 -0
- package/templates/src/jobs/README.md +3 -0
- package/templates/src/repositories/AGENTS.md +118 -0
- package/templates/src/repositories/tables/AGENTS.md +94 -0
- package/templates/src/repositories/tables/README.md +3 -0
- package/templates/src/repositories/views/AGENTS.md +25 -0
- package/templates/src/repositories/views/README.md +3 -0
- package/templates/src/sql/AGENTS.md +77 -0
- package/templates/src/sql/README.md +6 -0
- package/templates/tests/AGENTS.md +43 -150
- package/templates/tests/generated/AGENTS.md +16 -0
- package/templates/tests/smoke.test.ts +5 -0
- package/templates/tests/smoke.validation.test.ts +34 -0
- package/templates/tests/support/AGENTS.md +26 -0
- package/templates/tests/support/global-setup.ts +8 -23
- package/templates/tests/support/testkit-client.ts +13 -741
- package/templates/tsconfig.json +8 -1
- package/templates/ztd/AGENTS.md +11 -67
- package/templates/ztd/README.md +4 -13
- package/templates/ztd/ddl/AGENTS.md +34 -0
- package/templates/ztd/ddl/demo.sql +74 -0
- package/templates/src/repositories/user-accounts.ts +0 -179
- package/templates/tests/user-profiles.test.ts +0 -161
- package/templates/tests/writer-constraints.test.ts +0 -32
package/templates/README.md
CHANGED
|
@@ -1,217 +1,14 @@
|
|
|
1
1
|
# Zero Table Dependency Project
|
|
2
2
|
|
|
3
|
-
This project
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Only `ztd/ddl` is part of the template contract. Do not create or assume other `ztd` subdirectories unless the project explicitly adds them.
|
|
17
|
-
|
|
18
|
-
## Generated files (important)
|
|
19
|
-
|
|
20
|
-
`tests/generated/` is auto-generated and must never be committed to git.
|
|
21
|
-
After cloning the repository (or in a clean environment), run (strongly recommended):
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npx ztd ztd-config
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
If TypeScript reports missing modules or type errors because `tests/generated/` is missing, rerun `npx ztd ztd-config`.
|
|
28
|
-
|
|
29
|
-
`tests/generated/ztd-layout.generated.ts` declares the directories above so the CLI and your tests always point at the intended files. The authoritative directory remains `ztd/ddl/`; do not read or assume additional `ztd` subdirectories.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
# Optional SqlClient seam
|
|
34
|
-
|
|
35
|
-
If this project was initialized with `npx ztd init --with-sqlclient`, you'll also have `src/db/sql-client.ts`.
|
|
36
|
-
It defines a minimal `SqlClient` interface that repositories can depend on:
|
|
37
|
-
|
|
38
|
-
- Use it for tutorials and greenfield projects to keep repository SQL decoupled from drivers.
|
|
39
|
-
- Skip it when you already have a database abstraction (Prisma, Drizzle, Kysely, custom adapters).
|
|
40
|
-
- For `pg`, adapt `client.query(...)` so it returns a plain `T[]` row array that matches the interface.
|
|
41
|
-
- Prefer a shared client per worker process so tests and scripts do not reconnect on every query.
|
|
42
|
-
- Do not share a live connection across parallel workers; each worker should own its own shared client.
|
|
43
|
-
|
|
44
|
-
Example (driver-agnostic):
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
let sharedClient: SqlClient | undefined;
|
|
48
|
-
|
|
49
|
-
export function getSqlClient(): SqlClient {
|
|
50
|
-
if (!sharedClient) {
|
|
51
|
-
// Create the client once using your chosen driver (pg, mysql, etc.).
|
|
52
|
-
sharedClient = createSqlClientOnce();
|
|
53
|
-
}
|
|
54
|
-
return sharedClient;
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
# Mapper + writer sample
|
|
61
|
-
|
|
62
|
-
- This scaffold already exposes `src/repositories/user-accounts.ts`, which maps `public.user_account` rows together with optional `public.user_profile` data through `@rawsql-ts/sql-contract/mapper` and emits insert/update/remove helpers via `@rawsql-ts/sql-contract/writer`.
|
|
63
|
-
- The SQL used here is defined in `src/repositories/user-accounts.ts`; the template tests exercise that implementation and `ztd/ddl` is the authoritative source for every column and constraint.
|
|
64
|
-
- Two template tests demonstrate how to run the stitch:
|
|
65
|
-
- `tests/user-profiles.test.ts` seeds fixtures, executes the query through the mapper, and verifies the DTO shape.
|
|
66
|
-
- `tests/writer-constraints.test.ts` reads `userAccountWriterColumnSets` plus `tests/generated/ztd-row-map.generated.ts` so writer callers stay within the approved column set when referencing `public.user_account`.
|
|
67
|
-
- Regenerate `tests/generated/ztd-row-map.generated.ts` (`npx ztd ztd-config`) before running the example tests so the row map reflects any schema changes.
|
|
68
|
-
- The example tests require a real PostgreSQL connection via `DATABASE_URL`; they automatically skip when the variable is missing so local tooling stays fast.
|
|
69
|
-
|
|
70
|
-
# Principles
|
|
71
|
-
|
|
72
|
-
### 1. Humans own the *definitions*
|
|
73
|
-
- DDL (physical schema)
|
|
74
|
-
Only `ztd/ddl` is part of the template contract; other subdirectories should not be assumed.
|
|
75
|
-
|
|
76
|
-
### 2. AI owns the *implementation*
|
|
77
|
-
- Repository SQL generation
|
|
78
|
-
- Test fixture updates
|
|
79
|
-
- Intermediate TypeScript structures
|
|
80
|
-
- SQL rewriting, parameter binding, shape resolution
|
|
81
|
-
|
|
82
|
-
### 3. ZTD ensures these stay in sync
|
|
83
|
-
ZTD acts as the consistency layer ensuring:
|
|
84
|
-
- DDL → SQL shape consistency
|
|
85
|
-
- Do not rely on other directories unless the project explicitly adds them.
|
|
86
|
-
|
|
87
|
-
If any part diverges, ZTD tests fail deterministically.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
# Workflow Overview
|
|
92
|
-
|
|
93
|
-
Different tasks start from different entry points. Choose the workflow that matches what you want to change.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
# Workflow A — Starting From *DDL Changes*
|
|
98
|
-
(Adding tables/columns, changing constraints)
|
|
99
|
-
|
|
100
|
-
1. Edit files under `ztd/ddl/`.
|
|
101
|
-
2. Run:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npx ztd ztd-config
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
This regenerates `tests/generated/ztd-row-map.generated.ts` from the new schema.
|
|
108
|
-
|
|
109
|
-
3. Update repository SQL so it matches the new schema.
|
|
110
|
-
4. Update fixtures if shapes changed.
|
|
111
|
-
5. Run tests. Any schema mismatch will fail fast.
|
|
112
|
-
|
|
113
|
-
**Flow:**
|
|
114
|
-
**DDL -> repository SQL -> fixtures/tests -> application**
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
# Workflow B — Starting From *Repository Interface Changes*
|
|
119
|
-
(Adding a method, changing return types, etc.)
|
|
120
|
-
|
|
121
|
-
1. Modify the repository interface or class in `/src`.
|
|
122
|
-
2. Allow AI to generate the SQL needed to satisfy the interface.
|
|
123
|
-
3. If the query contradicts DDL, reconcile the authoritative definition before continuing.
|
|
124
|
-
4. Run ZTD tests to confirm logic is consistent.
|
|
125
|
-
5. Regenerate ZTD config if result shapes changed.
|
|
126
|
-
|
|
127
|
-
**Flow:**
|
|
128
|
-
**repository interface -> SQL -> tests**
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
# Workflow C — Starting From Repository SQL Logic Changes
|
|
133
|
-
(Fixing a bug, optimizing logic, rewriting a query)
|
|
134
|
-
|
|
135
|
-
1. Edit SQL inside the repository.
|
|
136
|
-
2. Run ZTD tests.
|
|
137
|
-
3. If the intended behavior changes, update the DDL before adjusting dependent logic and keep documentation aligned with the confirmed schema.
|
|
138
|
-
4. Update fixtures as necessary.
|
|
139
|
-
5. If SQL result shape changed, run:
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
npx ztd ztd-config
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Flow:**
|
|
146
|
-
**SQL -> fixtures/tests**
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
# Combined Real‑World Flow Examples
|
|
151
|
-
|
|
152
|
-
- **Add a new contract status**
|
|
153
|
-
DDL -> SQL -> config -> tests
|
|
154
|
-
|
|
155
|
-
- **Add a new table**
|
|
156
|
-
DDL -> config -> SQL -> fixtures -> tests
|
|
157
|
-
|
|
158
|
-
- **Fix business logic**
|
|
159
|
-
SQL -> tests
|
|
160
|
-
|
|
161
|
-
ZTD ensures all changes converge into a consistent, validated workflow.
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
# Human Responsibilities
|
|
165
|
-
|
|
166
|
-
- Humans maintain:
|
|
167
|
-
- Physical schema (`ddl`)
|
|
168
|
-
- High‑level repository interfaces
|
|
169
|
-
- Acceptance of AI-generated changes
|
|
170
|
-
|
|
171
|
-
Humans decide “what is correct.”
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
# AI Responsibilities
|
|
176
|
-
|
|
177
|
-
AI must:
|
|
178
|
-
|
|
179
|
-
- Use DDL as the **physical shape constraint** and primary source of truth.
|
|
180
|
-
- Do not assume any additional `ztd` directories exist unless a human explicitly creates them.
|
|
181
|
-
- Generate repository SQL consistent with DDL and the documented behavior.
|
|
182
|
-
- Regenerate fixtures and tests as instructed.
|
|
183
|
-
- Never modify `ztd/AGENTS.md` or `ztd/README.md` unless explicitly asked.
|
|
184
|
-
|
|
185
|
-
AI decides “how to implement” within those constraints.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
# ZTD CLI Responsibilities
|
|
190
|
-
|
|
191
|
-
ZTD CLI:
|
|
192
|
-
|
|
193
|
-
- Parses DDL files to build accurate table/column shapes
|
|
194
|
-
- Rewrites SQL with fixture-based CTE shadowing (via testkit adapters)
|
|
195
|
-
- Generates `ztd-row-map.generated.ts`
|
|
196
|
-
- Produces deterministic, parallelizable tests
|
|
197
|
-
|
|
198
|
-
ZTD is the verification engine guaranteeing correctness.
|
|
199
|
-
|
|
200
|
-
## Traditional execution mode
|
|
201
|
-
|
|
202
|
-
- Set `ZTD_EXECUTION_MODE=traditional` or pass `{ mode: 'traditional', traditional: { isolation: 'schema', cleanup: 'drop_schema' } }` when you need to run the tests against a real Postgres schema (locking, isolation, constraints). The helper still applies the DDL inside `ztd/ddl/`, loads the fixture rows into the schema, optionally executes `setupSql`, and carries out the chosen cleanup strategy (`drop_schema`, `custom_sql`, or `none`).
|
|
203
|
-
- Use `isolation: 'none'` if you need to target a schema that is already defined or if your SQL embeds schema qualifiers explicitly.
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
# Summary
|
|
208
|
-
|
|
209
|
-
ZTD enables a workflow where **humans define meaning**, **AI writes implementation**, and **tests guarantee correctness**.
|
|
210
|
-
|
|
211
|
-
The project layout and workflows above ensure long-term maintainability, clarity, and full reproducibility of SQL logic independent of physical database state.
|
|
212
|
-
|
|
213
|
-
## Recommended local verification
|
|
214
|
-
|
|
215
|
-
- `npx ztd ztd-config` (Recommended)
|
|
216
|
-
- `pnpm -C packages/ztd-cli test` (Recommended)
|
|
217
|
-
- `templates/tests/user-profiles.test.ts` runs only when `DATABASE_URL` is configured; otherwise it skips automatically.
|
|
3
|
+
This project uses Zero Table Dependency (ZTD) to keep SQL, DDL, and tests aligned.
|
|
4
|
+
|
|
5
|
+
Key folders:
|
|
6
|
+
- ztd/ddl: schema files (source of truth)
|
|
7
|
+
- src: application SQL and repositories
|
|
8
|
+
- tests: ZTD tests and support
|
|
9
|
+
|
|
10
|
+
Next steps:
|
|
11
|
+
1. Update `ztd/ddl/<schema>.sql` if needed.
|
|
12
|
+
2. Run `npx ztd ztd-config`.
|
|
13
|
+
3. Provide a SqlClient implementation.
|
|
14
|
+
4. Run tests (`pnpm test` or `npx vitest run`).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promise that resolves to the array of rows produced by an SQL query.
|
|
3
|
+
* @template T Shape of each row yielded by the SQL client.
|
|
4
|
+
* @example
|
|
5
|
+
* const rows: SqlQueryRows<{ id: number; name: string }> = client.query('SELECT id, name FROM users');
|
|
6
|
+
*/
|
|
7
|
+
export type SqlQueryRows<T> = Promise<T[]>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal SQL client interface required by the repository layer.
|
|
11
|
+
*
|
|
12
|
+
* - Production: adapt this interface to your preferred driver (pg, mysql2, etc.) and normalize the results to `T[]`.
|
|
13
|
+
* - Tests: replace the implementation with a mock, a fixture helper, or an adapter that follows this contract.
|
|
14
|
+
*
|
|
15
|
+
* Connection strategy note:
|
|
16
|
+
* - Prefer a shared client per worker process for better performance.
|
|
17
|
+
* - Do not share a live client across parallel workers without proper synchronization.
|
|
18
|
+
*/
|
|
19
|
+
export type SqlClient = {
|
|
20
|
+
query<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
21
|
+
text: string,
|
|
22
|
+
values?: readonly unknown[]
|
|
23
|
+
): SqlQueryRows<T>;
|
|
24
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# src AGENTS
|
|
2
|
+
|
|
3
|
+
This directory is runtime application code.
|
|
4
|
+
|
|
5
|
+
## Boundaries
|
|
6
|
+
|
|
7
|
+
- Code under "src/" MUST NOT import from:
|
|
8
|
+
- "tests/"
|
|
9
|
+
- "tests/generated/"
|
|
10
|
+
- any test-only helpers
|
|
11
|
+
- Runtime code MUST NOT depend on ZTD internals.
|
|
12
|
+
- Generated artifacts are test-only signals, not runtime dependencies.
|
|
13
|
+
|
|
14
|
+
## Implementation principles
|
|
15
|
+
|
|
16
|
+
- Keep modules small and explicit.
|
|
17
|
+
- Prefer explicit contracts over inference.
|
|
18
|
+
- Favor deterministic behavior and clear error surfaces.
|
|
19
|
+
|
|
20
|
+
## Verification (required)
|
|
21
|
+
|
|
22
|
+
After changes:
|
|
23
|
+
- Run the project typecheck command (example: "pnpm typecheck").
|
|
24
|
+
- Run relevant tests (example: "pnpm test" or a filtered command).
|
|
25
|
+
|
|
26
|
+
If you touched SQL contracts or catalog specs, run tests that exercise them.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# src/catalog AGENTS
|
|
2
|
+
|
|
3
|
+
This directory is runtime code.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Catalog defines named query entry points by binding:
|
|
8
|
+
- SQL assets ("src/sql/*.sql")
|
|
9
|
+
- input parameter contracts
|
|
10
|
+
- output DTO contracts
|
|
11
|
+
- validation and mapping behavior
|
|
12
|
+
- observability hooks (if supported)
|
|
13
|
+
|
|
14
|
+
## Directory roles (important)
|
|
15
|
+
|
|
16
|
+
- "src/catalog/specs": human-owned contracts (params + DTO + semantics)
|
|
17
|
+
- "src/catalog/runtime": AI-assisted runtime wiring (executors, helpers, sinks)
|
|
18
|
+
|
|
19
|
+
## Non-negotiable ownership
|
|
20
|
+
|
|
21
|
+
- Specs are contracts. Do not infer, guess, widen, or narrow them.
|
|
22
|
+
- Do not change params / DTO shapes in "specs" without explicit instruction.
|
|
23
|
+
|
|
24
|
+
## Boundaries
|
|
25
|
+
|
|
26
|
+
- Code under "src/catalog/" MUST NOT import from:
|
|
27
|
+
- "tests/"
|
|
28
|
+
- "tests/generated/"
|
|
29
|
+
- "ztd/"
|
|
30
|
+
- Do not depend on ZTD internals at runtime.
|
|
31
|
+
|
|
32
|
+
## Testing rule (required)
|
|
33
|
+
|
|
34
|
+
Every spec MUST have tests that verify:
|
|
35
|
+
- SQL executes under ZTD rewriting
|
|
36
|
+
- mapping/validation behavior is correct (success and failure)
|
|
37
|
+
- output DTO shape matches expectations
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# src/catalog/runtime AGENTS
|
|
2
|
+
|
|
3
|
+
This directory contains runtime wiring for catalog specs:
|
|
4
|
+
- parameter validation entrypoints
|
|
5
|
+
- row-to-DTO mapping
|
|
6
|
+
- output validation
|
|
7
|
+
|
|
8
|
+
## Runtime classification
|
|
9
|
+
|
|
10
|
+
- This is a runtime directory.
|
|
11
|
+
- Code here is executed/loaded by the application.
|
|
12
|
+
|
|
13
|
+
## Responsibilities (critical)
|
|
14
|
+
|
|
15
|
+
- Runtime MUST be the only place that:
|
|
16
|
+
- validates unknown inputs into typed params
|
|
17
|
+
- maps SQL rows (snake_case) into DTO objects
|
|
18
|
+
- validates DTO outputs before returning to repositories
|
|
19
|
+
- Runtime output validation MUST follow the output contract type (DTO or scalar).
|
|
20
|
+
- Command outputs MAY be scalar identifiers or `void`; DTO mapping is required only when the output contract is DTO.
|
|
21
|
+
|
|
22
|
+
Repositories MUST call runtime helpers (ensure*/map*) and MUST NOT bypass them.
|
|
23
|
+
|
|
24
|
+
## Validator requirement (required)
|
|
25
|
+
|
|
26
|
+
- A validator library (zod or arktype) is always available.
|
|
27
|
+
- Runtime MUST apply validators for:
|
|
28
|
+
- input params (unknown -> typed)
|
|
29
|
+
- outputs (mapped DTO -> validated DTO)
|
|
30
|
+
|
|
31
|
+
Do not rely on TypeScript types alone.
|
|
32
|
+
|
|
33
|
+
## SQL row normalization (important)
|
|
34
|
+
|
|
35
|
+
SQL drivers may return different runtime representations for the same column types.
|
|
36
|
+
Runtime MUST normalize driver-dependent values before validating DTOs.
|
|
37
|
+
|
|
38
|
+
Required normalization rules:
|
|
39
|
+
- Timestamp columns (e.g. timestamptz) may arrive as Date or string.
|
|
40
|
+
- Runtime MUST use `timestampFromDriver` from `@rawsql-ts/sql-contract`.
|
|
41
|
+
- Do NOT re-implement `normalizeTimestamp` locally.
|
|
42
|
+
- Do NOT call `new Date(...)` directly for driver-dependent timestamp normalization.
|
|
43
|
+
- Numeric columns may arrive as number, string, or bigint depending on driver.
|
|
44
|
+
- Normalization rules MUST be explicit per contract and MUST NOT be silent.
|
|
45
|
+
|
|
46
|
+
Never force SQL assets to encode driver-specific behavior.
|
|
47
|
+
|
|
48
|
+
## Mapping rules (required)
|
|
49
|
+
|
|
50
|
+
- Keep SQL assets snake_case and DTO-independent.
|
|
51
|
+
- Mapping occurs in runtime:
|
|
52
|
+
- snake_case row -> DTO-shaped object
|
|
53
|
+
- apply normalization
|
|
54
|
+
- validate via spec-owned validator
|
|
55
|
+
- DTO camelCase aliases in SQL are forbidden.
|
|
56
|
+
|
|
57
|
+
## Entry points (recommended)
|
|
58
|
+
|
|
59
|
+
Prefer these patterns:
|
|
60
|
+
- `ensureXxxParams(value: unknown): XxxParams` (validate inputs)
|
|
61
|
+
- `mapXxxRowToDto(row: XxxSqlRow): XxxDto` (normalize + validate outputs)
|
|
62
|
+
|
|
63
|
+
The `map*` functions MUST always validate before returning.
|
|
64
|
+
|
|
65
|
+
## Error behavior (required)
|
|
66
|
+
|
|
67
|
+
- Validation errors must fail fast with clear messages.
|
|
68
|
+
- Do not swallow validator errors.
|
|
69
|
+
- Do not silently coerce invalid values unless explicitly defined by the contract.
|
|
70
|
+
|
|
71
|
+
## Boundaries
|
|
72
|
+
|
|
73
|
+
- Do not perform database I/O here.
|
|
74
|
+
- Do not import from "tests/" or "tests/generated/".
|
|
75
|
+
- Do not define human-owned contracts here; they live in "src/catalog/specs".
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { timestampFromDriver as normalizeTimestamp } from '@rawsql-ts/sql-contract';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { parseSmokeOutput, type SmokeOutput } from '../specs/_smoke.spec';
|
|
2
|
+
import { normalizeTimestamp } from './_coercions';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validate runtime output against the catalog smoke invariant.
|
|
6
|
+
*/
|
|
7
|
+
export function ensureSmokeOutput(value: unknown): SmokeOutput {
|
|
8
|
+
// Normalize driver-dependent timestamp representations before contract validation.
|
|
9
|
+
if (isRecord(value) && 'createdAt' in value) {
|
|
10
|
+
return parseSmokeOutput({
|
|
11
|
+
...value,
|
|
12
|
+
createdAt: normalizeTimestamp(value.createdAt, 'createdAt')
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return parseSmokeOutput(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
20
|
+
return typeof value === 'object' && value !== null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# src/catalog/specs AGENTS
|
|
2
|
+
|
|
3
|
+
This directory defines query and command specifications (contracts).
|
|
4
|
+
|
|
5
|
+
## Runtime classification
|
|
6
|
+
|
|
7
|
+
- This is a runtime directory.
|
|
8
|
+
- Contents are human-owned contracts, not implementation details.
|
|
9
|
+
|
|
10
|
+
## Ownership (critical)
|
|
11
|
+
|
|
12
|
+
- Specs are human-owned.
|
|
13
|
+
- Input parameters are part of the domain contract.
|
|
14
|
+
- Output DTO shapes are part of the domain contract.
|
|
15
|
+
- Cardinality and semantics are part of the domain contract.
|
|
16
|
+
|
|
17
|
+
AI MUST NOT:
|
|
18
|
+
- infer missing parameters
|
|
19
|
+
- widen or narrow DTO shapes
|
|
20
|
+
- change nullability or optionality
|
|
21
|
+
- change CUD return shape implicitly
|
|
22
|
+
|
|
23
|
+
Changes require explicit human instruction.
|
|
24
|
+
|
|
25
|
+
## Validator requirement (required)
|
|
26
|
+
|
|
27
|
+
- A validator library (zod or arktype) is always available.
|
|
28
|
+
- Every spec MUST define validators for its public inputs/outputs.
|
|
29
|
+
- Validators are part of the contract and are human-owned.
|
|
30
|
+
|
|
31
|
+
## Driver variability policy (important)
|
|
32
|
+
|
|
33
|
+
Specs MUST NOT require SQL assets to encode driver-specific workarounds.
|
|
34
|
+
|
|
35
|
+
- Specs may acknowledge that SQL rows can vary in representation by driver
|
|
36
|
+
(e.g. timestamps as Date or string).
|
|
37
|
+
- Normalization for driver-dependent values is implemented in catalog runtime,
|
|
38
|
+
then validated against the spec.
|
|
39
|
+
|
|
40
|
+
Contracts define the final DTO shape.
|
|
41
|
+
Runtime defines normalization steps to reach that shape.
|
|
42
|
+
|
|
43
|
+
## What does NOT belong here
|
|
44
|
+
|
|
45
|
+
- No executors
|
|
46
|
+
- No database connections
|
|
47
|
+
- No ZTD internals
|
|
48
|
+
- No SQL camelCase aliasing rules (SQL rules live under src/sql)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type } from 'arktype';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validator invariant contract used to prove runtime validation wiring.
|
|
5
|
+
*
|
|
6
|
+
* This file is intentionally minimal and domain-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
export const smokeOutputSchema = type({
|
|
9
|
+
id: 'number.integer',
|
|
10
|
+
createdAt: 'Date'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export type SmokeOutput = ReturnType<typeof smokeOutputSchema>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse and validate an unknown runtime payload.
|
|
17
|
+
*/
|
|
18
|
+
export function parseSmokeOutput(value: unknown): SmokeOutput {
|
|
19
|
+
smokeOutputSchema.assert(value);
|
|
20
|
+
return value as SmokeOutput;
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validator invariant contract used to prove runtime validation wiring.
|
|
5
|
+
*
|
|
6
|
+
* This file is intentionally minimal and domain-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
export const smokeOutputSchema = z.object({
|
|
9
|
+
id: z.number().int(),
|
|
10
|
+
createdAt: z.date()
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export type SmokeOutput = z.infer<typeof smokeOutputSchema>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse and validate an unknown runtime payload.
|
|
17
|
+
*/
|
|
18
|
+
export function parseSmokeOutput(value: unknown): SmokeOutput {
|
|
19
|
+
return smokeOutputSchema.parse(value);
|
|
20
|
+
}
|
|
@@ -9,16 +9,16 @@ export type SqlQueryRows<T> = Promise<T[]>;
|
|
|
9
9
|
/**
|
|
10
10
|
* Minimal SQL client interface required by the repository layer.
|
|
11
11
|
*
|
|
12
|
-
* - Production: adapt
|
|
13
|
-
* - Tests:
|
|
12
|
+
* - Production: adapt this interface to your preferred driver (pg, mysql2, etc.) and normalize the results to `T[]`.
|
|
13
|
+
* - Tests: replace the implementation with a mock, a fixture helper, or an adapter that follows this contract.
|
|
14
14
|
*
|
|
15
15
|
* Connection strategy note:
|
|
16
|
-
* - Prefer a shared client per worker process for performance.
|
|
17
|
-
* - Do not share a live client across parallel workers.
|
|
16
|
+
* - Prefer a shared client per worker process for better performance.
|
|
17
|
+
* - Do not share a live client across parallel workers without proper synchronization.
|
|
18
18
|
*/
|
|
19
19
|
export type SqlClient = {
|
|
20
20
|
query<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
21
21
|
text: string,
|
|
22
|
-
values?: readonly unknown[]
|
|
22
|
+
values?: readonly unknown[] | Record<string, unknown>
|
|
23
23
|
): SqlQueryRows<T>;
|
|
24
24
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# src/jobs AGENTS
|
|
2
|
+
|
|
3
|
+
This folder contains script-like operations (procedural SQL execution).
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
- Maintenance jobs
|
|
8
|
+
- Data backfills
|
|
9
|
+
- Batch operations
|
|
10
|
+
- Temporary-table driven workflows
|
|
11
|
+
|
|
12
|
+
## Safety rules
|
|
13
|
+
|
|
14
|
+
- Be explicit about transaction boundaries.
|
|
15
|
+
- Prefer idempotent design (safe to rerun).
|
|
16
|
+
- Emit clear logs/events at start and end of the job.
|
|
17
|
+
|
|
18
|
+
## SQL usage
|
|
19
|
+
|
|
20
|
+
- It is acceptable to run multiple statements in a job (including temp tables).
|
|
21
|
+
- Avoid coupling job logic to tests-only helpers.
|
|
22
|
+
|
|
23
|
+
## Testing
|
|
24
|
+
|
|
25
|
+
- Prefer integration-style tests that verify observable outcomes.
|
|
26
|
+
- If the job is heavy, test smaller units or use fixtures to limit scope.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# src/repositories AGENTS
|
|
2
|
+
|
|
3
|
+
This directory contains repository implementations.
|
|
4
|
+
|
|
5
|
+
## Runtime classification
|
|
6
|
+
|
|
7
|
+
- This is a runtime directory.
|
|
8
|
+
- Code here is executed by the application.
|
|
9
|
+
|
|
10
|
+
## Responsibilities
|
|
11
|
+
|
|
12
|
+
Repositories orchestrate execution only.
|
|
13
|
+
|
|
14
|
+
They are responsible for:
|
|
15
|
+
- loading SQL assets from `src/sql`
|
|
16
|
+
- validating external inputs via catalog runtime
|
|
17
|
+
- binding parameters
|
|
18
|
+
- calling `SqlClient`
|
|
19
|
+
- mapping SQL rows to DTOs via catalog runtime
|
|
20
|
+
- returning application-facing results
|
|
21
|
+
|
|
22
|
+
Repositories MUST remain thin adapters over SQL.
|
|
23
|
+
|
|
24
|
+
Repositories MUST NOT:
|
|
25
|
+
- embed business rules
|
|
26
|
+
- infer schema or DTO structure
|
|
27
|
+
- compensate for driver limitations with extra queries
|
|
28
|
+
|
|
29
|
+
## Contract boundaries (important)
|
|
30
|
+
|
|
31
|
+
- Human-owned contracts live under "src/catalog/specs".
|
|
32
|
+
- Runtime normalization and validation live under "src/catalog/runtime".
|
|
33
|
+
- Repositories MUST NOT invent, infer, or modify contracts.
|
|
34
|
+
|
|
35
|
+
If a contract is insufficient, update the spec/runtime first.
|
|
36
|
+
|
|
37
|
+
## Mandatory runtime usage (required)
|
|
38
|
+
|
|
39
|
+
Repositories MUST use catalog runtime helpers:
|
|
40
|
+
- `ensure*Params` for all external inputs (`unknown -> typed`)
|
|
41
|
+
- `map*RowToDto` for row-to-DTO mapping and output validation
|
|
42
|
+
|
|
43
|
+
Repositories MUST NOT:
|
|
44
|
+
- call spec validators directly
|
|
45
|
+
- bypass runtime normalization
|
|
46
|
+
- return DTOs without runtime validation
|
|
47
|
+
- assume driver-specific runtime types (e.g. timestamps always `Date`)
|
|
48
|
+
|
|
49
|
+
## SQL rules
|
|
50
|
+
|
|
51
|
+
- SQL assets MUST remain DTO-independent.
|
|
52
|
+
- SQL files MUST use snake_case column names.
|
|
53
|
+
- Repositories MUST NOT push DTO aliasing into SQL.
|
|
54
|
+
- Inline SQL strings are forbidden.
|
|
55
|
+
- Repository SQL loading MUST be centralized in shared runtime/repository infrastructure.
|
|
56
|
+
- Repositories MUST reference SQL assets by stable logical key (example: `user/insert_user.sql`).
|
|
57
|
+
- Repository modules MUST NOT pass caller-relative paths (`./`, `../`) or absolute filesystem paths to SQL loaders.
|
|
58
|
+
- The shared loader MUST resolve logical keys against the `src/sql` root (or catalog SQL registry), never relative to the caller file location.
|
|
59
|
+
- Repository modules MUST NOT implement ad-hoc SQL file loading with `readFileSync`, `__dirname`, `import.meta.url`, or direct path resolution.
|
|
60
|
+
|
|
61
|
+
## CUD default policy (important)
|
|
62
|
+
|
|
63
|
+
Default policy for table-oriented CUD is:
|
|
64
|
+
- CREATE SQL MAY use `RETURNING` for identifier columns only.
|
|
65
|
+
- CREATE repository methods MUST return the generated identifier by default.
|
|
66
|
+
- CREATE repository methods MUST NOT perform a follow-up SELECT whose purpose is DTO return shaping.
|
|
67
|
+
- UPDATE/DELETE SQL MUST NOT use `RETURNING`.
|
|
68
|
+
- repositories do NOT perform follow-up SELECTs after UPDATE/DELETE.
|
|
69
|
+
- UPDATE/DELETE methods return `Promise<void>` by default.
|
|
70
|
+
|
|
71
|
+
If a CREATE method must return a DTO, that requirement MUST be explicitly declared in catalog specs by human instruction.
|
|
72
|
+
|
|
73
|
+
## CUD row count rules (non-negotiable)
|
|
74
|
+
|
|
75
|
+
- UPDATE and DELETE MUST rely on driver-reported affected row counts (`rowCount` or equivalent).
|
|
76
|
+
- Repositories MUST NOT use SQL `RETURNING` result presence/count as a substitute for affected row counts.
|
|
77
|
+
- Repositories MUST NOT emulate affected rows using:
|
|
78
|
+
- pre-check SELECT
|
|
79
|
+
- post-check SELECT
|
|
80
|
+
- retry loops
|
|
81
|
+
- shadow queries / CTE hacks
|
|
82
|
+
|
|
83
|
+
If `rowCount === 0`, repositories MUST throw an explicit error.
|
|
84
|
+
|
|
85
|
+
If the driver cannot report affected row counts:
|
|
86
|
+
- CUD verification is unsupported
|
|
87
|
+
- tests MUST fail fast
|
|
88
|
+
- do NOT add workaround logic in repositories
|
|
89
|
+
|
|
90
|
+
## Read (SELECT) rules
|
|
91
|
+
|
|
92
|
+
- SELECT methods may return:
|
|
93
|
+
- `T | null` for single-row queries
|
|
94
|
+
- `T[]` for multi-row queries
|
|
95
|
+
- Absence of rows is NOT an error unless specified by the contract.
|
|
96
|
+
|
|
97
|
+
## Error behavior
|
|
98
|
+
|
|
99
|
+
- Propagate validation errors as failures.
|
|
100
|
+
- Do not silently coerce invalid data.
|
|
101
|
+
- Error messages SHOULD include operation name and identifying parameters.
|
|
102
|
+
|
|
103
|
+
## Testing expectations
|
|
104
|
+
|
|
105
|
+
- Every public repository method MUST be covered by tests.
|
|
106
|
+
- Update/Delete tests MUST verify rowCount handling explicitly.
|
|
107
|
+
- Repository changes without tests are incomplete.
|
|
108
|
+
|
|
109
|
+
## Guiding principle
|
|
110
|
+
|
|
111
|
+
Repositories execute contracts.
|
|
112
|
+
They do not interpret intent.
|
|
113
|
+
|
|
114
|
+
If correctness depends on extra queries to "confirm" changes:
|
|
115
|
+
- the contract is wrong
|
|
116
|
+
- or the driver is unsupported
|
|
117
|
+
|
|
118
|
+
Fix the contract, not the repository.
|