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

# Decorators

Register metadata with the `ModelRegistry` at class definition time. The sync engine uses this for change tracking, serialization, delta processing, and schema hashing.

Enable decorators in `tsconfig.json`:

```json
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}
```

## Common options

Shared by several decorators. Individual sections reference this table.

| Option       | Type                 | Default | Description                                                                                                                     |
| ------------ | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `lazy`       | `boolean`            | `false` | Defers hydration until first access.                                                                                            |
| `serializer` | `PropertySerializer` | -       | Custom serializer for converting values to and from their stored representation. See [Custom serializers](#custom-serializers). |

## @ClientModel

Registers a synced model.

```ts
import { Model, ClientModel } from "@stratasync/core";

@ClientModel("Task")
class Task extends Model {
  // properties...
}
```

**Signature:** `ClientModel(modelName: string, options?: ModelOptions)`

### ModelOptions

| Option                  | Type              | Default     | Description                                                |
| ----------------------- | ----------------- | ----------- | ---------------------------------------------------------- |
| `loadStrategy`          | `LoadStrategy`    | `"instant"` | How and when the model data is fetched.                    |
| `partialLoadMode`       | `PartialLoadMode` | -           | Hydration priority for partial models.                     |
| `usedForPartialIndexes` | `boolean`         | -           | Whether this model is used for partial index dependencies. |
| `schemaVersion`         | `number`          | -           | Schema version for migration tracking.                     |
| `tableName`             | `string`          | -           | Database table name override.                              |
| `groupKey`              | `string`          | -           | Sync-group key field for multi-tenancy.                    |

### Load strategies

```ts
@ClientModel("Project", { loadStrategy: "instant" })
class Project extends Model {
  /* ... */
}

@ClientModel("Attachment", { loadStrategy: "lazy" })
class Attachment extends Model {
  /* ... */
}

@ClientModel("Comment", { loadStrategy: "partial" })
class Comment extends Model {
  /* ... */
}
```

| Strategy                | Behavior                                                           |
| ----------------------- | ------------------------------------------------------------------ |
| `"instant"`             | Loaded during bootstrap. Always available in memory.               |
| `"lazy"`                | Loaded on first access, cached thereafter.                         |
| `"partial"`             | Loaded on demand, partially hydrated.                              |
| `"explicitlyRequested"` | Never auto-loaded. Must be explicitly requested via `ensureModel`. |
| `"local"`               | Never synced. Only stored locally on the device.                   |

## @Property

Marks a synced property for change tracking and delta processing.

```ts
import { Model, ClientModel, Property } from "@stratasync/core";

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @Property()
  status = "todo";

  @Property({ lazy: true })
  description = "";

  @Property({ serializer: dateSerializer })
  dueDate: Date | null = null;
}
```

**Signature:** `Property(options?: PropertyOptions)`

### PropertyOptions

Supports [common options](#common-options) (`lazy`, `serializer`). No additional options.

### Custom serializers

Handle types that need conversion for storage:

```ts
import type { PropertySerializer } from "@stratasync/core";

const dateSerializer: PropertySerializer<Date | null> = {
  serialize(value: Date | null): unknown {
    return value ? value.toISOString() : null;
  },
  deserialize(value: unknown): Date | null {
    return typeof value === "string" ? new Date(value) : null;
  },
};

@ClientModel("Event")
class Event extends Model {
  @Property({ serializer: dateSerializer })
  startsAt: Date | null = null;
}
```

## @EphemeralProperty

Observable but **not persisted** to the server. Use for reactive local UI state.

```ts
@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @EphemeralProperty()
  isExpanded = false;

  @EphemeralProperty()
  localDraft = "";
}
```

**Signature:** `EphemeralProperty(options?: PropertyOptions)`

Same [common options](#common-options) as `@Property`. Participates in MobX observability but excluded from transactions and deltas.

## @Reference

Creates a foreign-key reference to another model with a lazy-resolved accessor.

```ts
@ClientModel("Task")
class Task extends Model {
  @Reference(() => Project, "tasks")
  project!: Project;
  // Also creates: projectId: string (the foreign key)
}
```

**Signature:** `Reference(modelFactory: () => ModelConstructor, inverseProperty?: string, options?: ReferenceOptions)`

| Parameter         | Description                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------ |
| `modelFactory`    | Factory function returning the referenced model constructor. Uses a factory to avoid circular deps.    |
| `inverseProperty` | Name of the inverse collection property on the referenced model (for example, `"tasks"` on `Project`). |

### ReferenceOptions

Supports [common options](#common-options) (`lazy`, `serializer`), plus:

| Option       | Type      | Default                                | Description                          |
| ------------ | --------- | -------------------------------------- | ------------------------------------ |
| `foreignKey` | `string`  | `"<propertyName>Id"`                   | Override the foreign key field name. |
| `nullable`   | `boolean` | -                                      | Whether the reference can be null.   |
| `indexed`    | `boolean` | `true` (when `inverseProperty` is set) | Whether to index this foreign key.   |

### Custom foreign key

```ts
@ClientModel("Task")
class Task extends Model {
  @Reference(() => User, undefined, { foreignKey: "ownerId" })
  assignee!: User;
  // Creates: ownerId: string (instead of default "assigneeId")
}
```

## @ManyToOne

Alias for `@Reference`. Accepts a model name string or factory function.

```ts
@ClientModel("Task")
class Task extends Model {
  @ManyToOne("Project", "tasks")
  project!: Project;

  @ManyToOne(() => User)
  assignee!: User;
}
```

**Signature:** `ManyToOne(modelNameOrFactory: string | (() => ModelConstructor), inverseProperty?: string, options?: ReferenceOptions)`

Options are the same as [`@Reference`](#referenceoptions).

## @OneToMany

Defines the parent side of a one-to-many relationship. Returns a `LazyCollection` of child models.

```ts
@ClientModel("Project")
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}
```

**Signature:** `OneToMany(options?: ReferenceCollectionOptions)`

### ReferenceCollectionOptions

Supports [common options](#common-options) (`lazy`, `serializer`), plus:

| Option       | Type      | Default | Description                                              |
| ------------ | --------- | ------- | -------------------------------------------------------- |
| `foreignKey` | `string`  | -       | Override the foreign key used for the collection lookup. |
| `indexed`    | `boolean` | -       | Whether the collection uses an index.                    |
| `nullable`   | `boolean` | -       | Whether the collection can be null.                      |

### Inverse side pairing

`@ManyToOne` on the child creates the foreign key; `@OneToMany` on the parent creates the collection.

```ts
@ClientModel("Project")
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}

@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @ManyToOne("Project", "tasks")
  project!: Project;
  // Creates projectId foreign key, which @OneToMany uses
}
```

## @BackReference

Defines an inverse lookup for relationships declared on the other side.

```ts
@ClientModel("Comment")
class Comment extends Model {
  @Property()
  body = "";

  @BackReference({ foreignKey: "commentId" })
  replies!: Reply[];
}
```

**Signature:** `BackReference(options?: BackReferenceOptions)`

### BackReferenceOptions

Supports [common options](#common-options) (`lazy`, `serializer`), plus:

| Option       | Type     | Default | Description                                 |
| ------------ | -------- | ------- | ------------------------------------------- |
| `foreignKey` | `string` | -       | The foreign key field on the related model. |

## @ReferenceArray

An ordered array of references, typically for many-to-many relationships through a join model.

```ts
@ClientModel("Task")
class Task extends Model {
  @Property()
  title = "";

  @ReferenceArray({ through: "TaskTag" })
  tags!: Tag[];
}
```

**Signature:** `ReferenceArray(options?: ReferenceArrayOptions)`

### ReferenceArrayOptions

Supports [common options](#common-options) (`lazy`, `serializer`), plus:

| Option    | Type     | Default | Description                                             |
| --------- | -------- | ------- | ------------------------------------------------------- |
| `through` | `string` | -       | Join/through model name for many-to-many relationships. |

## @ReferenceCollection

Alias for `@OneToMany`. Use whichever name reads better in your domain model. See [`@OneToMany`](#onetomany) for options and usage.

## Complete example

Every decorator in a minimal project management domain:

```ts
import {
  Model,
  ClientModel,
  Property,
  EphemeralProperty,
  ManyToOne,
  OneToMany,
  ReferenceArray,
} from "@stratasync/core";
import type { LazyCollection } from "@stratasync/core";

@ClientModel("Project", { loadStrategy: "instant" })
class Project extends Model {
  @Property()
  name = "";

  @OneToMany()
  tasks!: LazyCollection<Task>;
}

@ClientModel("Task", { loadStrategy: "instant" })
class Task extends Model {
  @Property()
  title = "";

  @Property({ serializer: dateSerializer })
  dueDate: Date | null = null;

  @ManyToOne("Project", "tasks")
  project!: Project;

  @ReferenceArray({ through: "TaskTag" })
  tags!: Tag[];

  @EphemeralProperty()
  isSelected = false;
}
```