Phase 0+1: Repo-Skelett für Cards-Greenfield
Strategie B (beschlossen 2026-05-08): Cards wird als eigenständige
föderierte App neu gebaut, ohne Code-Übernahme aus mana-monorepo.
Skelett enthält:
- apps/api: Hono+Bun mit /healthz, /version, Manifest-Endpoint, leere
pgSchema('cards'), Drizzle-Config, erstem Vitest
- apps/web: SvelteKit 2 + Svelte 5 (runes), Vite auf 3082
- packages/cards-domain: Pure-TS, CardType-Discriminated-Union,
SubIndex-Granularität für Reviews, Future-CardType-Set vorbereitet
- infrastructure/docker-compose.yml: Postgres 16 auf 5435
- app-manifest.json: v1.0.0, Verein-owned, beta-tier
- .github/workflows/ci.yml
- docs/LESSONS_FROM_MANA_MONOREPO.md (Read-Day-Output, 15 Lehren)
Pre-Flight für Phase 2 (Auth-Föderation): DNS cardecky.mana.how,
GitHub-Repo mana-ev/cards, Cards-App-Registrierung in mana-auth,
NPM_AUTH_TOKEN für Verdaccio.
Plan: mana/docs/playbooks/CARDS_GREENFIELD.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
8605b1b517
37 changed files with 1197 additions and 0 deletions
31
packages/cards-domain/package.json
Normal file
31
packages/cards-domain/package.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "@cards/domain",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Cards-Domain — Pure-TS-Modell: Card-Types, FSRS-Adapter, Cloze-Parser, Anki-Import-Helpers, zod-Schemas. Keine DB-, Framework- oder Hono/SvelteKit-Abhängigkeiten.",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./types": "./src/types.ts",
|
||||
"./fsrs": "./src/fsrs.ts",
|
||||
"./cloze": "./src/cloze.ts",
|
||||
"./schemas": "./src/schemas/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json --noEmit",
|
||||
"type-check": "tsc -p tsconfig.json --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"lint": "echo 'lint configured later'",
|
||||
"clean": "rm -rf dist .turbo coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"ts-fsrs": "^5.3.2",
|
||||
"zod": "3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "^2.1.0"
|
||||
}
|
||||
}
|
||||
8
packages/cards-domain/src/index.ts
Normal file
8
packages/cards-domain/src/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// @cards/domain — public API
|
||||
//
|
||||
// Pure-TS, keine DB/Framework-Abhängigkeiten. Wird vom apps/api
|
||||
// (Drizzle-Schemas) und apps/web (UI) gleichermaßen konsumiert.
|
||||
|
||||
export * from './types.ts';
|
||||
// export * from './fsrs.ts'; // Phase 3: FSRS-Adapter (ts-fsrs)
|
||||
// export * from './cloze.ts'; // Phase 8 oder später: Cloze-Parser
|
||||
7
packages/cards-domain/src/schemas/index.ts
Normal file
7
packages/cards-domain/src/schemas/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// Phase-3-Aufgabe: zod-Schemas für API-Inputs/Outputs.
|
||||
//
|
||||
// Konvention: ein zod-Schema pro Domain-Type (DeckSchema, CardSchema,
|
||||
// ReviewSchema). API-Routen rufen `Schema.parse()` für Input-Validation,
|
||||
// und `zod-to-json-schema` generiert die JSON-Schemas für mana-mcp/-share.
|
||||
|
||||
export {};
|
||||
87
packages/cards-domain/src/types.ts
Normal file
87
packages/cards-domain/src/types.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// Domain-Typen für Cards.
|
||||
//
|
||||
// Modellierung folgt den Lessons aus mana-monorepo
|
||||
// (siehe docs/LESSONS_FROM_MANA_MONOREPO.md):
|
||||
//
|
||||
// - CardType ist eine discriminated union.
|
||||
// - Card hat `fields: Record<string, string>` als generischen Slot.
|
||||
// - Pro Karte gibt es N Reviews mit `subIndex` (basic = 1, basic-reverse = 2,
|
||||
// cloze = 1 pro Cluster).
|
||||
// - cardReviews bleiben PLAINTEXT, weil der Scheduler täglich auf `due`
|
||||
// filtert.
|
||||
|
||||
/** Phase-1-MVP-Set; Cloze + Image-Occlusion in Phase 8+. */
|
||||
export type CardType = 'basic' | 'basic-reverse';
|
||||
|
||||
/** Voll geplantes Set (für Schemas vorbereitet, MVP nicht alle implementiert). */
|
||||
export type CardTypeFuture =
|
||||
| 'basic'
|
||||
| 'basic-reverse'
|
||||
| 'cloze'
|
||||
| 'type-in'
|
||||
| 'image-occlusion'
|
||||
| 'audio'
|
||||
| 'multiple-choice';
|
||||
|
||||
export type CardFields = Record<string, string>;
|
||||
|
||||
export type Deck = {
|
||||
id: string;
|
||||
user_id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
visibility: 'private' | 'space' | 'public';
|
||||
fsrs_settings: FsrsSettings;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Card = {
|
||||
id: string;
|
||||
deck_id: string;
|
||||
user_id: string;
|
||||
type: CardType;
|
||||
fields: CardFields;
|
||||
tags: string[];
|
||||
media_refs: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pro `(card_id, sub_index)` ein Review-Eintrag. Der Scheduler quert auf
|
||||
* `due <= now` täglich — das Feld bleibt deshalb plaintext und ist indiziert.
|
||||
*/
|
||||
export type Review = {
|
||||
card_id: string;
|
||||
sub_index: number;
|
||||
user_id: string;
|
||||
due: string;
|
||||
stability: number;
|
||||
difficulty: number;
|
||||
elapsed_days: number;
|
||||
scheduled_days: number;
|
||||
reps: number;
|
||||
lapses: number;
|
||||
state: 'new' | 'learning' | 'review' | 'relearning';
|
||||
last_review: string | null;
|
||||
};
|
||||
|
||||
export type StudySession = {
|
||||
id: string;
|
||||
user_id: string;
|
||||
deck_id: string;
|
||||
started_at: string;
|
||||
finished_at: string | null;
|
||||
cards_reviewed: number;
|
||||
cards_correct: number;
|
||||
};
|
||||
|
||||
/** Default-Konstanten aus ts-fsrs; per-Deck-Overrides möglich. */
|
||||
export type FsrsSettings = {
|
||||
requestRetention?: number;
|
||||
maximumInterval?: number;
|
||||
w?: number[];
|
||||
enableFuzz?: boolean;
|
||||
};
|
||||
12
packages/cards-domain/tsconfig.json
Normal file
12
packages/cards-domain/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*", "tests/**/*"],
|
||||
"exclude": ["node_modules", "dist", ".turbo"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue