npm package: @kniddo/types
Description: Shared type definitions for Kniddo projects
Latest version: 0.1.2

# @kniddo/types

Shared type definitions for Kniddo projects. Uses **JSON Schema as the single source of truth** with generators for both TypeScript interfaces and Dart base classes.

## Architecture

```
schemas/*.schema.json          (source of truth)
        │
        ├──► ts-generator  ──► src/generated/models/*.ts   (TypeScript interfaces)
        │                          │
        │                          └──► tsc ──► dist/      (compiled JS + .d.ts)
        │
        └──► dart-generator ──► kniddo_flutter/lib/models/generated/*_base.g.dart
                                                           (Dart abstract base classes)
```

### Dart Generation Strategy

**"Generate base, extend manually"** — the generator produces `_FooBase` abstract classes with fields, `fromJSON`, `encodeBase`, and `props`. The developer writes `class Foo extends _FooBase` in a separate file, adding behavior, factory constructors, and custom logic. Generated files use Dart's `part`/`part of` mechanism so they share the host file's library scope.

## Project Structure

```
kniddo_types/
├── schemas/
│   ├── enums/              # Enum schemas (5 files)
│   └── models/             # Model schemas (22 files)
├── generators/
│   ├── generate.ts         # CLI entry point (--ts, --dart, --all, --validate)
│   ├── schema-loader.ts    # Load schemas, resolve $ref, topological sort
│   ├── ts-generator.ts     # JSON Schema → TypeScript interfaces
│   ├── dart-generator.ts   # JSON Schema → Dart base classes
│   └── type-mapping.ts     # Shared type resolution utilities
├── src/
│   ├── generated/          # Auto-generated TypeScript (do not edit)
│   │   ├── models/         # Generated interfaces
│   │   └── index.ts        # Barrel export
│   ├── constants/          # Hand-written constants (db collections, needle sizes)
│   ├── api/                # Hand-written API request/response types
│   └── index.ts            # Main package export
├── scripts/
│   └── validate-dart.ts    # Dart ↔ TypeScript type shape validator
├── package.json
└── tsconfig.json
```

## Scripts

```bash
npm run generate          # Generate TypeScript + Dart
npm run generate:ts       # TypeScript only
npm run generate:dart     # Dart only
npm run generate:validate # Validate schemas with ajv
npm run build             # Generate TypeScript + compile with tsc
npm run clean             # Remove dist/ and src/generated/
npm run validate-dart     # Validate Dart ↔ TS structural compatibility
```

## Consuming Projects

| Project | Integration |
|---------|------------|
| **kniddo_web** | `"@kniddo/types": "file:../kniddo_types"` in package.json + Vite alias |
| **kniddo_firebase** | `"@kniddo/types": "file:../../kniddo_types"` in functions/package.json |
| **kniddo_flutter** | Dart base classes generated directly into `lib/models/generated/` |

## Schema Format

Standard [JSON Schema (draft 2020-12)](https://json-schema.org/draft/2020-12/schema) with custom extensions:

### Custom Extensions

| Key | Purpose | Example |
|-----|---------|---------|
| `x-kniddo-format` | Semantic type hint | `"timestamp"` → Dart `DateTime`, `"duration-seconds"` → Dart `Duration` |
| `x-kniddo-extends` | Schema inheritance | `{ "$ref": "./yarn-info.schema.json" }` |
| `x-kniddo-dart-type` | Override Dart type | `"int"` to force `int` instead of `num` |
| `x-kniddo-dart-class` | Override Dart class name | `"Measurement"` on MeasurementSystem |
| `x-kniddo-dart-field` | Override Dart/Firestore field key | `"quanties"` (preserves Firestore typo) |
| `x-kniddo-dart-exclude` | Skip Dart generation | `true` on CounterColourSetID |
| `x-kniddo-ts-extra-type` | Add union member in TS | `"string"` → `PatternStatus \| string` |

### Type Mapping

| JSON Schema | TypeScript | Dart |
|---|---|---|
| `"type": "string"` | `string` | `String` |
| `"type": "number"` | `number` | `num` |
| `"type": "boolean"` | `boolean` | `bool` |
| `"type": "array", "items": X` | `X[]` | `List<X>` |
| `"type": "object", "additionalProperties": V` | `Record<string, V>` | `Map<String, V>` |
| `"enum": [...]` | `'a' \| 'b' \| ...` | `enum { a, b, ... }` |
| `x-kniddo-format: "timestamp"` | `number` | `DateTime` |
| `x-kniddo-format: "duration-seconds"` | `number` | `Duration` |

### Example Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "type": "object",
  "properties": {
    "id": { "type": "string" },
    "displayName": { "type": "string" },
    "email": { "type": "string" },
    "role": { "$ref": "../enums/user-role.schema.json" },
    "updatedTime": { "type": "number", "x-kniddo-format": "timestamp" },
    "createdTime": { "type": "number", "x-kniddo-format": "timestamp" }
  },
  "required": ["id", "displayName", "email", "role", "updatedTime", "createdTime"]
}
```

## What Stays Hand-Written

| Item | Why |
|---|---|
| `src/constants/db-collections.ts` | Runtime collection names, not data shape |
| `src/constants/needle-sizes.ts` | Runtime lookup data with helper functions |
| `src/api/pattern-api.ts` | Small API surface, low ROI for schema |
| Flutter model subclasses | Custom behavior, factory constructors, legacy migration |
| `CounterColourSetID` in Dart | Complex display name mapping |