docs(schema): Documentation of first change
Creation of specs for openspec and opencode to start working
This commit is contained in:
parent
0e46bf8f97
commit
d1dbf3fe77
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-06-03
|
||||
47
openspec/changes/add-shared-schema-package/design.md
Normal file
47
openspec/changes/add-shared-schema-package/design.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
## Context
|
||||
|
||||
The monorepo currently has three workspace roots: `apps/api` (NestJS), `apps/web` (Nuxt), and `packages/schema` (empty placeholder). The API and web apps share no type definitions, validation logic, or database schema — each would need to duplicate these. The `packages/schema` directory exists but has no `package.json`, no `tsconfig.json`, and no source files.
|
||||
|
||||
The chosen stack is:
|
||||
|
||||
- **Drizzle ORM** (PostgreSQL) for database table definitions, relations, and migrations
|
||||
- **Superstruct** for runtime schema validation (request payloads, configs, etc.)
|
||||
- **TypeScript** types derived from both Drizzle and Superstruct schemas
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Create a working `@workspace/schema` package with proper `package.json`, `tsconfig.json`, and build pipeline
|
||||
- Define Drizzle table schemas, relations, and enums targeting PostgreSQL
|
||||
- Define Superstruct validators for runtime data validation
|
||||
- Export derived TypeScript types that both `api` and `web` can consume
|
||||
- Wire the package into the Turborepo build graph so it builds before its consumers
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- No migrations or database connections — this package only provides schema definitions
|
||||
- No API endpoints or web UI — this is purely a data layer package
|
||||
- No initial table definitions — the package structure and conventions will be set up; actual tables come in follow-up changes
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Package name `@workspace/schema`** — follows the existing `packages/` directory pattern; scoped to avoid conflicts with npm; private: true in package.json.
|
||||
|
||||
2. **Entry points split by concern** — three source entry points (or one barrel export):
|
||||
- `src/drizzle/` — Drizzle table definitions (`pgTable`, relations, enums)
|
||||
- `src/validation/` — Superstruct schemas (`object()`, `string()`, etc.)
|
||||
- `src/types/` — Re-exported TypeScript types (inferred from Drizzle and Superstruct)
|
||||
|
||||
Single `src/index.ts` barrel re-exports everything. The `exports` field in `package.json` may expose sub-paths if the package grows large.
|
||||
|
||||
3. **Drizzle schema is the source of truth for DB structure** — Superstruct validators mirror the same shapes but can be more permissive (e.g., optional fields for partial updates). TypeScript types are inferred from both.
|
||||
|
||||
4. **`tsconfig.json` extends the root** — no root tsconfig exists yet, so `packages/schema` gets its own with `module: nodenext`, `target: ES2023`, strict mode, and declaration output.
|
||||
|
||||
5. **Build via `tsc`** — simple `tsc` build; no bundler needed since it's consumed internally by the monorepo.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Duplication risk]** Superstruct schemas and Drizzle definitions could drift apart. Mitigation: Superstruct validators should reference Drizzle-derived types where possible, and tests should assert consistency.
|
||||
- **[Build order]** Turborepo's `^build` dependsOn already handles topological order, so no config changes needed — just ensure api/web list `@workspace/schema` in dependencies.
|
||||
31
openspec/changes/add-shared-schema-package/proposal.md
Normal file
31
openspec/changes/add-shared-schema-package/proposal.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
## Why
|
||||
|
||||
The monorepo needs a single source of truth for business schema (database tables, validation rules, and shared types) that both the API and web apps can import. Currently `packages/schema` is an empty placeholder with no package.json or source files, forcing each app to duplicate type definitions and validation logic.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Populate `packages/schema` with a private npm package (`@workspace/schema`) including `package.json`, `tsconfig.json`, and source entry points
|
||||
- Add Drizzle ORM schema definitions for PostgreSQL (tables, relations, enums)
|
||||
- Add Superstruct-based runtime validators for request/response shapes
|
||||
- Export shared TypeScript types derived from both Drizzle and Superstruct definitions
|
||||
- Add `packages/schema` as a workspace dependency in both `apps/api` and `apps/web`
|
||||
- Update Turborepo pipeline so `schema` build runs before dependent apps
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `database-schema`: Drizzle ORM table definitions, relations, and enums targeting PostgreSQL
|
||||
- `validation`: Superstruct schema definitions for runtime validation of data shapes
|
||||
- `shared-types`: TypeScript type exports re-exported from Drizzle and Superstruct schemas
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
None — this is the first real content in `packages/schema`.
|
||||
|
||||
## Impact
|
||||
|
||||
- `packages/schema` will get a `package.json` and build output; its contents become importable by other workspace packages
|
||||
- `apps/api` and `apps/web` gain a new workspace dependency
|
||||
- `turbo.json` needs no changes — `^build` already handles topological build order
|
||||
- No existing code is removed or modified; this is purely additive
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Define PostgreSQL tables with Drizzle ORM
|
||||
|
||||
The package SHALL export Drizzle ORM table definitions using `pgTable` from `drizzle-orm/pg-core`, with proper column types, constraints, and defaults targeting PostgreSQL.
|
||||
|
||||
#### Scenario: Table exports are available
|
||||
|
||||
- **WHEN** another package imports from `@workspace/schema`
|
||||
- **THEN** all Drizzle table definitions SHALL be accessible as named exports
|
||||
|
||||
#### Scenario: Table uses standard column types
|
||||
|
||||
- **WHEN** a column is defined in a Drizzle table
|
||||
- **THEN** it SHALL use a `drizzle-orm/pg-core` column type (e.g., `text()`, `integer()`, `timestamp()`, `uuid()`, `boolean()`)
|
||||
|
||||
### Requirement: Define PostgreSQL enums
|
||||
|
||||
The package SHALL support PostgreSQL enum definitions using `pgEnum` from `drizzle-orm/pg-core` for constrained string fields.
|
||||
|
||||
#### Scenario: Enum is defined and exportable
|
||||
|
||||
- **WHEN** an enum is created with `pgEnum`
|
||||
- **THEN** it SHALL be exportable and usable as a column type in table definitions
|
||||
|
||||
### Requirement: Define table relations
|
||||
|
||||
The package SHALL define Drizzle relations (`relations` from `drizzle-orm`) between tables, supporting one-to-many, many-to-one, and many-to-many relationships.
|
||||
|
||||
#### Scenario: Relation links two tables
|
||||
|
||||
- **WHEN** a relation is defined between two tables
|
||||
- **THEN** the related fields SHALL be queryable via Drizzle's relational query API
|
||||
|
||||
### Requirement: All schemas are co-located
|
||||
|
||||
The package SHALL colocate Drizzle table definitions, enums, and relations within the same directory structure under `src/drizzle/`.
|
||||
|
||||
#### Scenario: Single import for database concerns
|
||||
|
||||
- **WHEN** a consumer needs database schema
|
||||
- **THEN** it SHALL import from `@workspace/schema` without accessing internal paths
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Export TypeScript types inferred from Drizzle schemas
|
||||
|
||||
The package SHALL export TypeScript types inferred from Drizzle ORM table definitions using `InferSelectModel` and `InferInsertModel` from `drizzle-orm`, providing both row and insert shapes.
|
||||
|
||||
#### Scenario: Select type is available
|
||||
|
||||
- **WHEN** a table is defined in Drizzle
|
||||
- **THEN** an `InferSelectModel` type SHALL be exported for that table
|
||||
|
||||
#### Scenario: Insert type is available
|
||||
|
||||
- **WHEN** a table is defined in Drizzle
|
||||
- **THEN** an `InferInsertModel` type SHALL be exported for that table
|
||||
|
||||
### Requirement: Export TypeScript types inferred from Superstruct schemas
|
||||
|
||||
The package SHALL export TypeScript types inferred from Superstruct schemas using `Infer` from `superstruct`, so that runtime validation and compile-time types stay in sync.
|
||||
|
||||
#### Scenario: Infer type is available
|
||||
|
||||
- **WHEN** a Superstruct schema is defined
|
||||
- **THEN** an `Infer` type SHALL be exported for that schema
|
||||
|
||||
### Requirement: Type re-exports via barrel
|
||||
|
||||
All TypeScript types SHALL be re-exported from a single barrel entry point (`src/index.ts`) so consumers import from `@workspace/schema` without deep path references.
|
||||
|
||||
#### Scenario: Single import for all types
|
||||
|
||||
- **WHEN** a consumer imports from `@workspace/schema`
|
||||
- **THEN** all type exports SHALL be available from that single entry point
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Define Superstruct validators for data shapes
|
||||
|
||||
The package SHALL export Superstruct schema definitions for validating runtime data shapes (request payloads, configuration objects, etc.).
|
||||
|
||||
#### Scenario: Validator validates correct data
|
||||
|
||||
- **WHEN** data matching the schema is passed to the validator
|
||||
- **THEN** the validator SHALL return the validated data without throwing
|
||||
|
||||
#### Scenario: Validator rejects invalid data
|
||||
|
||||
- **WHEN** data not matching the schema is passed to the validator
|
||||
- **THEN** the validator SHALL throw a `StructError` with details about the validation failure
|
||||
|
||||
### Requirement: Validator composability
|
||||
|
||||
Superstruct schemas SHALL be composable — schemas can reference other schemas for nested object validation.
|
||||
|
||||
#### Scenario: Nested object validation
|
||||
|
||||
- **WHEN** a schema defines a nested object field using another Superstruct schema
|
||||
- **THEN** the nested schema SHALL be applied during validation of the parent
|
||||
|
||||
### Requirement: Optional and default fields
|
||||
|
||||
Superstruct schemas SHALL support optional fields (`optional()`) and fields with default values (`defaulted()`) for partial updates or configuration defaults.
|
||||
|
||||
#### Scenario: Optional field omitted
|
||||
|
||||
- **WHEN** an optional field is omitted from input data
|
||||
- **THEN** the validator SHALL accept the data and not require the field
|
||||
|
||||
#### Scenario: Default value applied
|
||||
|
||||
- **WHEN** a field with a default value is omitted from input data
|
||||
- **THEN** the validator SHALL apply the default value in the returned data
|
||||
38
openspec/changes/add-shared-schema-package/tasks.md
Normal file
38
openspec/changes/add-shared-schema-package/tasks.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
## 1. Package scaffolding
|
||||
|
||||
- [x] 1.1 Create `packages/schema/package.json` with name `@workspace/schema`, private: true, and TypeScript build script
|
||||
- [x] 1.2 Create `packages/schema/tsconfig.json` extending the API's config pattern (nodenext, ES2023, strictNullChecks, declaration)
|
||||
- [x] 1.3 Add `drizzle-orm`, `superstruct`, and `@types/node` as dependencies in the new package
|
||||
- [ ] 1.4 Create `packages/schema/src/index.ts` barrel file re-exporting from sub-modules
|
||||
- [ ] 1.5 Add `@workspace/schema` as a workspace dependency in `apps/api/package.json`
|
||||
- [ ] 1.6 Add `@workspace/schema` as a workspace dependency in `apps/web/package.json`
|
||||
- [ ] 1.7 Verify `pnpm install` resolves the workspace dependency and `pnpm build` compiles the package
|
||||
|
||||
## 2. Drizzle schema definitions
|
||||
|
||||
- [ ] 2.1 Create `packages/schema/src/drizzle/` directory structure
|
||||
- [ ] 2.2 Define `pgEnum` exports for any PostgreSQL enums needed
|
||||
- [ ] 2.3 Define initial Drizzle tables with `pgTable`, proper column types, constraints, and defaults
|
||||
- [ ] 2.4 Define `relations()` between tables for relational queries
|
||||
- [ ] 2.5 Export all table definitions, enums, and relations from the Drizzle sub-module barrel
|
||||
|
||||
## 3. Superstruct validation schemas
|
||||
|
||||
- [ ] 3.1 Create `packages/schema/src/validation/` directory structure
|
||||
- [ ] 3.2 Define Superstruct schemas for data shapes, with proper type constraints
|
||||
- [ ] 3.3 Support nested/optional/defaulted fields via Superstruct combinators
|
||||
- [ ] 3.4 Export all validation schemas from the validation sub-module barrel
|
||||
|
||||
## 4. Shared TypeScript types
|
||||
|
||||
- [ ] 4.1 Create `packages/schema/src/types/` directory structure
|
||||
- [ ] 4.2 Export `InferSelectModel` and `InferInsertModel` types derived from all Drizzle tables
|
||||
- [ ] 4.3 Export `Infer` types derived from all Superstruct schemas
|
||||
- [ ] 4.4 Re-export all types through the top-level `src/index.ts` barrel
|
||||
|
||||
## 5. Wire into monorepo
|
||||
|
||||
- [ ] 5.1 Verify Turborepo topological build order — `turbo.json` `^build` should already handle `schema` → `api`/`web`
|
||||
- [ ] 5.2 Run `pnpm build` from root to confirm clean compilation
|
||||
- [ ] 5.3 Run `pnpm --filter=api test` to confirm no regressions
|
||||
- [ ] 5.4 Run `pnpm check-types` to confirm type-checking passes across all workspaces
|
||||
Loading…
Reference in a new issue