Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ca2861e
add CLAUDE.md with project context and engineering conventions
emilwojtaszek Feb 12, 2026
28c2b01
add .tool-versions to pin Node 24.3.0 for asdf
emilwojtaszek Feb 12, 2026
453e4d7
add HTTP microservice for PDF generation from XML
emilwojtaszek Feb 12, 2026
da149a2
support KSeF number and QR code via request headers
emilwojtaszek Feb 12, 2026
f0e638d
update README with API headers and curl examples
emilwojtaszek Feb 12, 2026
dd2084c
add GitHub Actions workflow to publish Docker image to GHCR
emilwojtaszek Feb 12, 2026
e8c1480
add pdfmake-html-renderer dependency
emilwojtaszek Feb 12, 2026
b3413be
extract buildDocDefinition functions from FA and UPO generators
emilwojtaszek Feb 12, 2026
6812527
add HTML output format via pdfmake-html-renderer
emilwojtaszek Feb 12, 2026
52d1e09
add POST /generate/html endpoint to server
emilwojtaszek Feb 12, 2026
b138239
move HTML rendering to server, add buildInvoiceDocDefinition
emilwojtaszek Feb 12, 2026
885dad4
add integration tests for POST /generate/html endpoint
emilwojtaszek Feb 12, 2026
128c5cd
update README with HTML generation endpoint and examples
emilwojtaszek Feb 12, 2026
dd17374
rename /generate to /generate/pdf for consistent API paths
emilwojtaszek Feb 12, 2026
1c73f34
harden server: payload size limit, sanitized errors, graceful teardown
emilwojtaszek Feb 12, 2026
1a86e98
address review feedback: request IDs, port 0, error assertions, docs
emilwojtaszek Feb 12, 2026
4cce8ee
fix clone URL in README, add KSeF header propagation test
emilwojtaszek Feb 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
.git
*.md
.idea
.vscode
coverage
.vitest
.cache
temp
out
*.tsbuildinfo
.claude
43 changes: 43 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build and publish Docker image

on:
push:
branches: [main]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/ksef-pdf

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=sha,prefix=

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 24.3.0
92 changes: 92 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# CLAUDE.md — ksef-pdf-generator

## Why This Project Exists

The goal is to generate invoice PDFs without building and maintaining a custom template system that breaks every time the layout needs to change. Instead, this project leverages the Polish government's KSeF (Krajowy System e-Faktur) open-source PDF generators — they handle all the rendering logic from structured XML. The XML schema is the template.

The missing piece is a thin HTTP service that wraps this library so other systems can send invoice XML and get back a PDF. That's the end-game: **XML in, PDF out, via a simple API**.

## Project Overview

TypeScript library for generating PDF visualizations of KSeF invoices and UPO confirmations from XML files. Built with Vite, pdfmake, and xml-js.

**Architecture:** Layered — Public API (`src/index.ts`) -> business logic (`src/lib-public/`) -> shared utilities (`src/shared/`) -> external libs (pdfmake, xml-js).

**Domain:** Polish e-invoicing schemas FA(1), FA(2), FA(3), UPO v4.2/v4.3. Generator directories: `FA1/`, `FA2/`, `FA3/`, `UPO4_2/`, `UPO4_3/`.

## Commands

```bash
npm run dev # Dev server (port 5173)
npm run build # Production build (ES + UMD)
npm run type # TypeScript type checking
npm run test # Watch mode tests (vitest)
npm run test:ui # Interactive test UI
npm run test:ci # CI tests with coverage
```

## Code Conventions

**TypeScript:**
- Strict mode enabled
- Explicit return types on all functions (`@typescript-eslint/explicit-function-return-type: error`)
- Explicit member accessibility on classes (`@typescript-eslint/explicit-member-accessibility: warn`)
- `@typescript-eslint/no-explicit-any` is off — use sparingly and only when schema types are genuinely dynamic

**Prettier** (`.prettierrc.json`):
- 110 char print width, single quotes, trailing commas (`es5`), 2-space indent, semicolons

**ESLint** (`eslint.config.mts`):
- Curly braces always required (`curly: error`)
- Blank line after variable declarations (`@stylistic/padding-line-between-statements`)
- Member ordering enforced (`@typescript-eslint/member-ordering`)

**Naming:**
- Polish-English naming matching XML schema structure (e.g., `Naglowek`, `Wiersze`, `Stopka`, `Rozliczenie`, `Platnosc`, `Adres`)
- Types/interfaces in `types/` folders and `*.types.ts` files
- Co-located test files: `Component.ts` -> `Component.spec.ts`

## Architecture & SOLID Principles

- **Single Responsibility:** Each generator file handles one PDF section (e.g., `Adres.ts` = address, `Platnosc.ts` = payment, `Wiersze.ts` = line items)
- **Open/Closed:** New schema versions (FA4, UPO v5) should add new generator directories under `src/lib-public/generators/`, not modify existing ones
- **Liskov Substitution:** Generator functions share consistent signatures (`(invoice, additionalData?) => Content[]`)
- **Interface Segregation:** Type definitions are schema-specific (`fa1.types.ts`, `fa2.types.ts`, `fa3.types.ts`)
- **Dependency Inversion:** Generators depend on abstractions (`Content[]`, `FormatTyp`) not pdfmake internals

## DRY Practices

- Reuse shared utilities from `src/shared/PDF-functions.ts` (`formatText`, `createLabelText`, `createSection`, `createHeader`, `generateTable`, `getContentTable`)
- Reuse common generators from `src/lib-public/generators/common/` (Naglowek, Wiersze, Stopka, Rozliczenie, DaneFaKorygowanej, Zalaczniki)
- Reuse lookup functions from `src/shared/generators/common/functions.ts`
- Constants centralized in `src/shared/consts/const.ts`
- Use `FormatTyp` enum from `src/shared/enums/common.enum.ts` for all text formatting — do not inline formatting logic

## TDD Approach

- Write tests BEFORE implementation (Red-Green-Refactor)
- Co-locate test files: `Component.ts` -> `Component.spec.ts`
- Test structure: `describe('functionName')` with `it('expected behavior')`
- Test edge cases: undefined inputs, empty `_text`, invalid XML values
- Use Vitest globals (`describe`, `it`, `expect`) — no imports needed
- Mock external dependencies via `vi.spyOn` / `vi.fn`
- Run `npm run test:ci` to verify coverage before committing

## Technical Documentation

- All new public functions must have explicit TypeScript return types (ESLint enforced)
- Type definitions serve as documentation — keep them accurate and up-to-date
- Generator function names must match the XML schema section they handle
- Document non-obvious business logic with inline comments (tax rules, schema-specific behavior)

## Key Files Reference

| File | Purpose |
|------|---------|
| `src/index.ts` | Library entry point |
| `src/lib-public/generate-invoice.ts` | Invoice routing logic |
| `src/shared/PDF-functions.ts` | Shared PDF utility functions |
| `src/shared/generators/common/functions.ts` | Common lookup/helper functions |
| `src/shared/consts/const.ts` | Centralized constants |
| `src/shared/enums/common.enum.ts` | Shared enums (FormatTyp, etc.) |
| `src/lib-public/generators/common/` | Common generators reused across schema versions |
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Build stage
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Runtime stage — only jsdom is needed (pdfmake + xml-js are bundled in dist)
FROM node:22-alpine
WORKDIR /app
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
COPY --from=build /app/dist ./dist
COPY --from=build /app/server ./server
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
USER app
EXPOSE 3001
HEALTHCHECK CMD wget -qO- http://localhost:3001/health || exit 1
CMD ["node", "server/index.js"]
Loading