> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myme.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Typed models

> Typed domain models and custom-type codegen

## Typed domain models

Every core type has a generated typed wrapper: `CoreNote`, `CoreBookmark`, `CoreTask`, `CoreHighlight`, `CoreEvent`, `CoreMedia` and its eight subtypes, `CoreEntity` / `CoreEntityPerson` / `CoreEntityPlace`, `CoreFile` / `CoreFileImage` / `CoreFileAudio` / `CoreFileVideo`. 21 in total.

Each wrapper conforms to the `MarfaItem` protocol — `typeIdentifier`, a failable `init?(from: Item)` that validates the type string and required fields, typed property accessors, and `toProperties()` for creating new items.

```swift theme={null}
let item = try await client.items.get(id: noteId)

guard let note = CoreNote(from: item) else {
  throw UnexpectedType()
}

print(note.body)           // typed String — required
print(note.title ?? "")    // typed String? — optional
print(note.version)        // from MarfaItem default accessors

// To create a new item from a domain type:
let newItem = try await client.items.create(
  CreateItemInput(type: CoreNote.typeIdentifier, properties: note.toProperties())
)
```

Subtype-aware: `CoreMediaArticle(from: item)` returns `nil` unless `item.type == "core.media.article"`. Required fields are non-optional (`CoreMediaArticle.title`, `.body`); optional fields surface as `T?`.

## Custom-type codegen

Apps that register their own types (`myapp.booking`, `myapp.user`, …) can generate Swift wrappers in the same shape as `CoreNote` and friends. The generated structs conform to `MarfaItem`, expose typed property accessors, a failable `init?(from: Item)`, and `toProperties()` for round-trip with `client.items.create` / `update`. Parent fields are flat-inlined, so a `myapp.booking` extending `core.note` exposes `body`, `title`, and the booking-specific fields on the same struct.

### 1. Configure

Drop a `marfa-codegen.json` at your repo root:

```jsonc theme={null}
{
  "schema": 1,
  "source": { "mode": "local", "directory": "MarfaTypes" },
  "output": { "directory": "Sources/MyApp/MarfaTypes/Generated", "accessLevel": "public" },
  "types": { "include": ["myapp.*"] }
}
```

Drop one `<type.id>.json` file per type under `MarfaTypes/`:

```json theme={null}
{
  "id": "myapp.booking",
  "parent": "core.note",
  "version": 1,
  "fields": {
    "start_at": { "type": "string", "format": "datetime" },
    "party_size": { "type": "integer" }
  },
  "required": ["start_at"]
}
```

For `"mode": "live"`, replace `directory` with `cacheDirectory` and let the sync executable populate the cache from `GET /types`.

### 2. Generate

Three entry points:

```bash theme={null}
# SwiftPM command plugin — sandboxed, one shot.
swift package --allow-writing-to-package-directory generate-marfa-custom-types

# Same plugin, but pull schemas from a live server first.
swift package --allow-writing-to-package-directory --allow-network-connections all \
  generate-marfa-custom-types --sync

# Executables — CI-friendly, no sandbox prompts.
swift run codegen-custom-types
MARFA_API_URL=… MARFA_API_KEY=… swift run sync-custom-types
```

The `--sync` flow and `sync-custom-types` executable both call `GET /types`, so `MARFA_API_KEY` must carry the `list_types` permission. Pure-local codegen needs no credentials.

`core.*` ids are always excluded from generation regardless of include/exclude globs — core wrappers ship with the SDK, so a misconfigured glob can't clobber them.

### 3. Use the generated types

```swift theme={null}
import SwiftUI
import MarfaSDK

struct BookingDetail: View {
  let item: Item

  var body: some View {
    if let booking = MyappBooking(from: item) {
      VStack(alignment: .leading) {
        Text(booking.title ?? "Untitled")
        Text(booking.startAt)
        Text("Party of \(booking.partySize ?? 0)")
      }
    } else {
      Text("Not a booking")
    }
  }
}

let created = try await client.items.create(
  CreateItemInput(
    type: MyappBooking.typeIdentifier,
    properties: [
      "start_at": .string("2026-05-01T18:00:00Z"),
      "party_size": .int(4),
      "body": .string("Dinner with J and S"),
    ]
  )
)
```

### 4. Keep generated files fresh

Commit the generated files. Add a freshness check to CI so a schema drift fails the build instead of going unnoticed:

```yaml theme={null}
- run: |
    swift run codegen-custom-types
    git diff --exit-code -- Sources/MyApp/MarfaTypes/Generated
```
