# İzinler ve Migration

> Düğüm izinleri (drag, delete, duplicate, edit) ve katman sırası, resolvePermissions, kilitli düğümler; MigrationConfig ile eski sayfa verisini yeni şemaya güvenle yükseltme.
>
> Kaynak: https://tecof-app-dev.vercel.app/editor-permissions · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20

## İzin modeli

İzinler varsayılan olarak **açıktır**; kısıtlamaya bir bayrağı `false` yaparak katılırsınız.

```ts
interface Permissions {
  drag: boolean;      // kanvas ve katmanlarda taşıma
  delete: boolean;    // tekli/toplu silme ve kesme
  duplicate: boolean; // çoğaltma ve yeni id ile yapıştırma
  edit: boolean;      // Inspector alanlarının düzenlenmesi
  [key: string]: boolean; // host’a özel ek izinler
}
```

Bir düğümün etkin izni katmanların birleşimidir; sonraki katman öncekini ezer:

```text
varsayılan < config.permissions < component.permissions < component.resolvePermissions(props) < düğümün _locked kilidi
```

```ts
const editorConfig: StudioConfig = {
  permissions: { delete: false },            // genel: hiçbir şey silinemez
  components: {
    Header: {
      permissions: { drag: false },          // header taşınamaz
      resolvePermissions: (props) => ({ edit: !props.locked }),
      render: (p) => <header className={p.className} />,
    },
    Hero: {
      permissions: { delete: true },         // Hero için silmeye yeniden izin ver
      render: (p) => <section className={p.className} />,
    },
  },
};
```

- Kısıtlar **motor seviyesinde** uygulanır: düğme gizlemekle kalmaz; klavye kısayolları (`⌘D`, `Delete`), sağ tık menüsü ve pano işlemleri de aynı kurala uyar.
- `edit: false` Inspector alanlarını salt okunur yapar.
- Delete izni olmayan düğümde **kes** işlemi düz kopyalamaya döner.
- Eksik anahtar ya da `resolvePermissions`’ın fırlattığı hata izinleri **sıkılaştırmaz**; o katman bir alttakinden devralınır. Hatalı bir çözümleyici kullanıcıyı kilitlemez.
- Katmanlar panelindeki kilit (`_locked`) aynı izin sistemini kullanır. Kilitli düğüm seçilebilir kalır (kilidi açabilmek için).

### Dinamik izinler

```ts
resolvePermissions?: (props: any, ctx: { changed: Record<string, boolean>; lastProps: Record<string, any> | null }) => Partial<Permissions>;
```

`ctx.changed` son çözümden beri değişen prop’ları, `ctx.lastProps` önceki anlık görüntüyü verir (ilk çalıştırmada `null`); yalnız ilgili alan değiştiğinde yeniden hesap yapmak için kullanın.

## Veri migration’ı

Bir bileşenin tip adı ya da prop şeması değiştiğinde önceden kaydedilmiş sayfalar eski şemada kalır. `migrations`, belge editörde yüklenirken **ve** `TecofRender` ile render edilirken otomatik uygulanır.

```ts
interface MigrationConfig {
  version?: number;                                   // hedef şema sürümü
  renameComponents?: Record<string, string>;          // { eskiTip: yeniTip }
  transformProps?: Record<string, (props: any) => any>; // YENİ tip adına göre
  migrate?: (doc: TecofDocument) => TecofDocument;     // son, serbest geçiş
}
```

```ts
const editorConfig: StudioConfig = {
  components,
  migrations: {
    version: 3,
    renameComponents: { OldHero: "Hero" },
    transformProps: {
      // Rename SONRASI tipe göre aranır: anahtar "Hero"
      Hero: (props) => ({ ...props, eyebrow: props.eyebrow ?? "", title: props.headline ?? props.title }),
    },
    migrate: (doc) => doc,
  },
};
```

### Çalışma sırası

1. `renameComponents` ile her düğümün `type`’ı değiştirilir.
2. Yeni tip adına göre `transformProps[type]` çalışır; düğüm `id`’si her zaman korunur.
3. Varsa tüm belgeye `migrate(doc)` uygulanır.
4. `version` tanımlıysa hedef sürüm `root.props._schemaVersion`’a yazılır.

### İdempotentlik

`version` verildiyse, `_schemaVersion`’ı zaten o sürümde ya da üstünde olan belge **dokunulmadan** döner. `version` verilmezse geçiş her yüklemede çalışır; bu durumda `transformProps` ve `migrate` kendi başlarına idempotent olmalıdır. Bir dönüşüm hata fırlatırsa o düğüm/belge değişmeden bırakılır; tek bir hata sayfayı kilitlemez.

> [!TIP] Zone anahtarları etkilenmez
> Zone anahtarları düğüm **id**’sine göre tutulur (`nodeId:slotAdı`), tipe göre değil. `renameComponents` hiçbir zaman zone anahtarlarının yeniden yazılmasını gerektirmez.
