@ksefnik/shared
Schematy Zod, typy TypeScript, interfejsy pluginów. Fundament bez zależności runtime (poza zod).
Ksefnik jest projektem open-source i przyjmujemy kontrybucje. Ten przewodnik opisuje jak skonfigurować środowisko, jakich konwencji przestrzegamy i jak wygląda proces review.
Forkuj repozytorium na GitHubie — kliknij “Fork” na stronie CodeFormers-it/ksefnik.
Sklonuj swojego forka:
git clone https://github.com/YOUR-USER/ksefnik.gitcd ksefnikZainstaluj zależności (wymagany pnpm i Node.js 22+):
pnpm installZbuduj wszystkie pakiety:
pnpm buildUruchom testy — upewnij się, że wszystko przechodzi:
pnpm testUtwórz branch dla swojej zmiany:
git checkout -b feature/my-featureKsefnik to monorepo pnpm workspace. Każdy pakiet ma jasno określoną odpowiedzialność:
@ksefnik/shared
Schematy Zod, typy TypeScript, interfejsy pluginów. Fundament bez zależności runtime (poza zod).
@ksefnik/core
Fasada SDK, silnik uzgadniania (6-pass pipeline), parsery bankowe. Cała logika biznesowa.
@ksefnik/http
Klient HTTP do KSeF API v2 — retry, cache, adapter pattern.
@ksefnik/simulator
Offline mock KSeF API do testów — scenariusze happy path, timeout, expired session.
@ksefnik/mcp
Serwer MCP (Model Context Protocol) — cienka warstwa na @ksefnik/core.
@ksefnik/cli
CLI (Commander.js + @clack/prompts) → kompilowane do standalone binary przez Bun.
ksefnik/├── packages/│ ├── shared/ @ksefnik/shared — Schematy Zod, typy, interfejsy pluginów│ ├── core/ @ksefnik/core — Fasada SDK, silnik uzgadniania, parsery bankowe│ ├── http/ @ksefnik/http — Klient HTTP do KSeF API v2│ ├── simulator/ @ksefnik/simulator — Offline mock KSeF API│ └── mcp/ @ksefnik/mcp — Serwer MCP (cienka warstwa na core)├── apps/│ ├── cli/ @ksefnik/cli — CLI → standalone binary│ └── docs/ Dokumentacja — Astro Starlight├── pnpm-workspace.yaml├── package.json└── vitest.workspace.ts| Komenda | Opis |
|---|---|
pnpm dev | Watch mode dla wszystkich pakietów |
pnpm build | Budowanie wszystkich pakietów |
pnpm test | Uruchomienie Vitest |
pnpm typecheck | Sprawdzenie typów we wszystkich pakietach |
Cały kod jest pisany w TypeScript z włączonym strict mode. Nie używamy any — jeśli typ jest nieznany, używamy unknown i walidujemy przez Zod.
Wszystkie modele domenowe (faktura, transakcja bankowa, wynik dopasowania) to schematy Zod. Typy TypeScript inferujemy z nich:
import { z } from 'zod'
export const InvoiceSchema = z.object({ ksefId: z.string(), nip: z.string(), amount: z.number().int(), // grosze! issueDate: z.string(),})
export type Invoice = z.infer<typeof InvoiceSchema>Wszystkie kwoty przechowujemy jako liczby całkowite w groszach (1 PLN = 100 groszy). Nigdy nie używamy float do pieniędzy.
// Dobrzeconst amount = 12345 // 123,45 PLN
// Źleconst amount = 123.45Projekt używa wyłącznie ESM ("type": "module" w package.json). Nie używamy require().
Używamy Prettier bez ESLint. Przed commitem uruchom:
pnpm prettier --write .Target branch — wszystkie PR-y celują w main.
CI musi przejść — pipeline sprawdza build, typecheck i testy. PR z czerwonym CI nie przejdzie review.
Review — PRy przegląda @luke-cf (CODEOWNERS). Typowy czas review: 1-2 dni robocze.
Conventional commits — używamy prefiksów w wiadomościach commitów:
| Prefix | Kiedy |
|---|---|
feat | Nowa funkcjonalność |
fix | Poprawka buga |
refactor | Zmiana kodu bez zmiany zachowania |
docs | Zmiany w dokumentacji |
test | Dodanie lub zmiana testów |
chore | Zmiany w konfiguracji, CI, tooling |
git commit -m "feat: add ING CSV parser"git commit -m "fix: handle empty NIP in bank transaction"git commit -m "docs: add contributing guide"Poniższe zmiany nie zostaną zaakceptowane — nie dlatego, że są złe, ale dlatego, że wykraczają poza scope projektu:
Pluginy to najlepszy sposób na rozszerzenie ksefnik bez modyfikacji core. Każdy plugin implementuje interfejs KsefPlugin z @ksefnik/shared:
import type { KsefPlugin, ReconciliationPass } from '@ksefnik/shared'
const myPlugin: KsefPlugin = { name: '@my-org/ksefnik-custom-pass', version: '1.0.0',
reconciliationPasses: [ { name: 'payment-pattern-match', order: 650, // Pluginy: order 600+ async execute(invoices, transactions, existingMatches) { // Twoja logika dopasowania return newMatches }, }, ],}
export default myPlugin