@appaflytech/zeyrek-ai-mcp 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js CHANGED
@@ -20,6 +20,7 @@ export const moduleNames = [
20
20
  'folder',
21
21
  'file',
22
22
  'access',
23
+ 'translation',
23
24
  'template',
24
25
  'organization',
25
26
  'dependency',
@@ -27,7 +28,7 @@ export const moduleNames = [
27
28
  ];
28
29
  export const modulePresets = {
29
30
  core: ['platform', 'project', 'application', 'datasource', 'entity', 'row', 'microservice', 'query', 'workflow', 'automation', 'dto', 'view', 'dependency'],
30
- studio: ['platform', 'page', 'component', 'flow', 'layout', 'variable', 'folder', 'file', 'access'],
31
+ studio: ['platform', 'page', 'component', 'flow', 'layout', 'variable', 'folder', 'file', 'access', 'translation'],
31
32
  admin: ['platform', 'template', 'organization', 'deployment']
32
33
  };
33
34
  function expandModuleNames(entries) {
@@ -328,7 +328,8 @@ export const componentKnowledge = {
328
328
  behavior: [
329
329
  "change olayında runtime machine.setLanguage(value) çağırır: seçim localStorage'a (zeyrek:{appSlug}:lang) yazılır ve çeviri bağlı bütün prop'lar SAYFA YENİLENMEDEN yeniden çözülür.",
330
330
  'Projede dil tanımlı değilse liste boş çizilir — önce proje dillerini (Translations paneli) kur.'
331
- ]
331
+ ],
332
+ guide: 'translation'
332
333
  },
333
334
  otpinput: {
334
335
  summary: 'Tek haneli kutulardan oluşan tek kullanımlık kod girişi (2–12 hane); yapıştırma kutulara dağılır.',
@@ -71,7 +71,10 @@ export const componentEventContracts = {
71
71
  // — 2026-08-25 eşleme turu: ng-apps-ui'da olay YAYAN ama burada kaydı olmayan komponentler.
72
72
  // Yükler kaynaktaki dispatch('emit', { name, data }) / machine.onEmit(id, { event, data })
73
73
  // çağrılarından birebir çıkarıldı. Olay yaymayan komponentler (badge · breadcrumb · jsonviewer ·
74
- // notification · progress · qrcode · slot · tags · text) bilinçli olarak kayıtsızdır.
74
+ // progress · qrcode · slot · tags · text) bilinçli olarak kayıtsızdır. `notification` bu listede
75
+ // YANLIŞLIKLA yer almıştı — Notification.svelte hem timeout'ta hem kapatma tıklamasında
76
+ // dispatch('emit', { name:'toggled', data:{isHidden:true}, setter:true }) çağırıyor (düzeltme
77
+ // 2026-08-25c, `docs/20-komponent-bilgi-katmani.md` §9).
75
78
  autocomplete: {
76
79
  mount: NO_DATA,
77
80
  change: valueChange('string (YAZILAN metin — option değeri değil)'),
@@ -107,6 +110,7 @@ export const componentEventContracts = {
107
110
  emptystate: { actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "aksiyonun kendi flow'u zaten doğrudan çalışır" } },
108
111
  iframe: { mount: NO_DATA },
109
112
  'language-selector': { change: { payload: { value: 'string (dil kodu)' }, reads: 'runtime ayrıca machine.setLanguage(value) çağırır' } },
113
+ notification: { toggled: { payload: { isHidden: 'boolean' } } },
110
114
  otpinput: {
111
115
  mount: NO_DATA,
112
116
  change: valueChange('string (birleşik kod)'),
@@ -64,10 +64,11 @@ export function entityOutline(definition) {
64
64
  dbType: field.dbType,
65
65
  nullable: field.nullable,
66
66
  relationType: field.relationType ?? 'none',
67
- relationId: field.relationId ?? null
67
+ relationId: field.relationId ?? null,
68
+ isLocalized: Boolean(field.isLocalized)
68
69
  })),
69
70
  view: 'outline',
70
- hint: "Bir alanın tam gövdesi için view:'part', part:'fields[slug=<slug>]'; değişiklik için entity_patch."
71
+ hint: "Bir alanın tam gövdesi için view:'part', part:'fields[slug=<slug>]'; değişiklik için entity_patch. isLocalized alanlar için platform_guide('entity') §'Çoklu dilli alan'."
71
72
  };
72
73
  }
73
74
  const segmentPattern = /^(\w+)(?:\[(?:(\d+)|(\w+)=([^\]]+))\])?$/;
@@ -186,7 +186,39 @@ Girdi sınırları (`IncrementalCodeInput.TryParse`; ihlal edilirse nesne geçer
186
186
  - `datasource_list.dataTypes` çıktısında bu tipi ayrı bir grup olarak görürsün; listede
187
187
  görmezsen yine de geçerlidir (rehberdeki "dataTypes'a güvenme" uyarısı burada da geçerli).
188
188
 
189
- ## Primary key
189
+ ## Çoklu dilli alan (`isLocalized`)
190
+
191
+ Bir alanı çoklu dilli yapmak için `dbType`/`commonType` değişmez — alan tanımına `"isLocalized":
192
+ true` eklenir (`FieldDto.IsLocalized`, generic `definition` içinde geçer, gateway'de ayrı bir
193
+ parametre YOKTUR). Kısıt (`FieldDtoValidator`): `isLocalized: true` iken `primaryKey` · `identity`
194
+ · `unique` `false` olmalı ve `relationType: 'none'` olmalı — bunlardan biri doluysa platform
195
+ reddeder.
196
+
197
+ **Platform bunun için ayrı bir uç açmaz.** Bir entity'de `isLocalized` alanı olduğunda (create ya
198
+ da update'te) platform otomatik olarak `{entity_slug}_localization` adlı **companion bir entity**
199
+ kurar/genişletir:
200
+
201
+ ```
202
+ id Guid, PK, identity
203
+ {entity_slug}_{parent_pk_slug} RELATION (one-to-many → ana entity, otomatik FK)
204
+ language Text
205
+ ...isLocalized:true olan alanlar (ana tanımdan BİREBİR taşınır)
206
+ ```
207
+
208
+ artı `(relation, language)` üzerinde unique index. Yani bir satırın Türkçe/İngilizce değerleri
209
+ **ayrı satırlardır**, ana tabloda değil bu companion entity'de yaşar.
210
+
211
+ **Kullanım (tamamı mevcut genel araçlarla, yeni tool GEREKMEZ):**
212
+
213
+ ```
214
+ 1. entity_get (ana entity, view:'outline') → isLocalized alanları görürsün
215
+ 2. entity_list (aynı datasource) → "{slug}_localization" entity'sini bul, id'sini al
216
+ 3. row_create/row_update (companion entity) → { "<fk_slug>": "<ana satır id>", "language": "tr", "<alan_slug>": "..." }
217
+ 4. row_list (companion entity, filtre yok — selectedFields ile daralt) → dile göre satırları oku
218
+ ```
219
+
220
+ `{fk_slug}` companion entity'nin `entity_get`'inde görünen ilişki alanının slug'ıdır (ana entity
221
+ adından türetilir); satırı elle kurmadan önce mutlaka `entity_get` ile doğrula, tahmin etme.
190
222
 
191
223
  ```jsonc
192
224
  // GUID anahtar
@@ -74,6 +74,10 @@ mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`
74
74
  `Data = RedirectPage.Slug`). Sabit `'/hastalar'` yazarsan sayfa slug'ı değişince bağlantı
75
75
  sessizce kırılır; `pageId` referansı değişiklikten etkilenmez. Aynısı `flow` prop'u için
76
76
  `type: 'flow'` + `ref: <flowId>`, dosya için `type: 'file'` + `ref: <fileId>`.
77
+ - **Çoklu dilli metin için `type: 'translation'` + `ref: <çeviri anahtarı>`.** Runtime `data`'yı
78
+ her turda çözümlenmiş metinle EZER — anahtar yalnız `ref`'te yaşar, `data`'ya yazıp
79
+ güvenme (`code` prop'uyla aynı davranış). Tam şekil, fallback zinciri ve `translation_*`
80
+ tool'ları için `platform_guide('translation')`.
77
81
  - **`blocks[].size`, komponentin `component_get`'teki `maxSize`'ını aşamaz.** Dikkat: katalog
78
82
  varsayılanı aşabiliyor (`page-header` default `h:8`, `maxSize` `h:4`) — varsayılanı körü körüne
79
83
  kopyalama, doğrulayıcı `"Height of 'page-header' must be between 1 and 4."` ile reddeder.
@@ -1339,7 +1343,7 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
1339
1343
  | Kart görünümü | `card` — **kapsayıcı DEĞİLDİR**, çocuk kabul etmez (`not-droppable`) ve yüksekliği 10-50 aralığındadır. Kutu içinde serbest içerik için `container` kullan. |
1340
1344
  | Katman | `modal` · `drawer` (şema kökünde — Kural 1d) |
1341
1345
  | Geri bildirim | `notification` (sayfa içi kutu) · `toast` (geçici bildirim; isOpen yükselen kenarı) · `form-validation-errors` · `confirm-bar` · `result` (işlem sonucu ekranı) · `emptystate` (boş liste; ikon YA DA görsel + aksiyon düğmeleri) |
1342
- | Gezinme / başlık | `breadcrumb` · `breadcrumb-title` (başlık + daralan iz) · `page-header` · `header` · `sidebar` · `auth-layout` · `language-selector` (dil değiştirici) |
1346
+ | Gezinme / başlık | `breadcrumb` · `breadcrumb-title` (başlık + daralan iz) · `page-header` · `header` · `sidebar` · `auth-layout` · `language-selector` (dil değiştirici — çoklu dil kurulumu `platform_guide('translation')`) |
1343
1347
  | Sunum | `text` · `html` · `image` · `video` · `icon` · `qrcode` · `tags` · `avatar` · `badge` (sayaç “99+” / nokta / durum metni) · `carousel` · `iframe` (sandbox’lı gömme) · `jsonviewer` (katlanabilir JSON; JSON METNİNİ de parse eder) · `audit-basic` · `audit-history` |
1344
1348
 
1345
1349
  > **`chart` komponenti VAR (2026-08-25 ölçümü) — bu satır düzeltildi.** Rehberin önceki hâli
@@ -0,0 +1,80 @@
1
+ # Çoklu Dil Rehberi (page/content translations)
2
+
3
+ Sayfa içeriğini (metin, buton etiketi, placeholder…) çoklu dilli yapan özellik. Studio panelinin
4
+ kendi arayüz dili (`i18n.md`, yalnız frontend'i ilgilendirir) ile **karıştırma** — bu rehber
5
+ **son kullanıcının gördüğü sayfa metnini** kapsar. Kaynak: `TranslationsController.cs`
6
+ (`zeyrek-backend/src/NgApps.Tenant.API/Controllers/`) + `zeyrek-frontend/docs/features/page-translations.md`.
7
+
8
+ ## Model
9
+
10
+ - Çeviri kaydı **application başına** tutulur (`(applicationId, language, key)` başına bir satır);
11
+ aynı application'daki tüm sayfalar aynı anahtar kümesini paylaşır.
12
+ - Bir kayıt isteğe bağlı bir **`componentId`**'ye bağlanabilir — o komponent silinince kaydı da
13
+ kademeli silinir. Salt organizasyon amaçlıdır; runtime çözümlemeyi etkilemez (çözümleme yalnız
14
+ `key`'e bakar).
15
+ - Proje **dilleri** ve **varsayılan dil** application değil **proje** düzeyinde ayardır
16
+ (`project_get`/`project_update` `languages`/`defaultLanguage`) ve tam **ISO 639-1** kod
17
+ kümesinden seçilir (`ProjectConstants.SupportedLanguages`, ~184 kod). `translation_update`'e
18
+ proje dillerinde olmayan bir kod verirsen platform isteği reddeder — önce `project_update` ile
19
+ dili ekle.
20
+
21
+ ## Araçlar
22
+
23
+ | Tool | Ne yapar |
24
+ |---|---|
25
+ | `translation_list` | Application'ın tüm `(language, key, value)` kayıtlarını düz liste döner |
26
+ | `translation_update` | `keys` (ekle/güncelle, `componentId` opsiyonel) ve/veya `deletedKeys` (sil) — ikisi de aynı çağrıda verilebilir, en az biri dolu olmalı |
27
+ | `project_update` | `languages`/`defaultLanguage` — proje dillerini kurar/değiştirir |
28
+
29
+ ```jsonc
30
+ // yeni anahtar aç / var olanı güncelle
31
+ { "applicationId": "<app id>", "keys": [
32
+ { "key": "home.welcome", "values": { "tr": "Hoş geldiniz", "en": "Welcome" } }
33
+ ] }
34
+
35
+ // bir anahtarı sil, bir başkasını aynı çağrıda güncelle
36
+ { "applicationId": "<app id>",
37
+ "keys": [{ "key": "home.title", "values": { "tr": "Ana sayfa" } }],
38
+ "deletedKeys": ["home.old_banner"] }
39
+ ```
40
+
41
+ ## Bir prop'u çeviriye bağlamak
42
+
43
+ Sayfa şemasında (`platform_guide('page')` Kural 1) çeviriye bağlı bir prop **Prop nesnesinin**
44
+ `type:'translation'` varyantıdır — `PropValueType.Translation` (`DesignComponentProperty.cs`)
45
+ backend'de zaten tanımlı, Studio'nun "Translate" aksiyonunun ürettiği şekil budur:
46
+
47
+ ```jsonc
48
+ { "op": "replace_prop", "target": "welcome_text", "prop": "text",
49
+ "value": { "type": "translation", "ref": "home.welcome", "data": "Hoş geldiniz" } }
50
+ ```
51
+
52
+ - **Anahtar `ref`'tedir, `data` DEĞİL.** Runtime her `getNodeProps` turunda `data`'yı
53
+ `resolveTranslation(...)` sonucuyla EZER (`code` prop'larıyla aynı davranış — `page.md` Kural 1c
54
+ bunun `code` karşılığını anlatır). `ref`'i okumadan/yazmadan `data`'ya güvenme; `data` yalnız
55
+ Studio'nun sakladığı önceki statik değerdir (fallback/geri dönüş amaçlı, çözümlemeyi ETKİLEMEZ).
56
+ - **Fallback zinciri:** aktif dil → projenin varsayılan dili → anahtarın kendisi. Boş string de
57
+ "eksik" sayılır ve zincirde bir alt basamağa düşer — bir anahtar hiçbir zaman boş render olmaz.
58
+ - Hangi prop'lar çeviriye bağlanabilir platformca kısıtlanmaz (Studio'da yalnız `text`/`long-text`
59
+ tipli prop'larda "Translate" aksiyonu gösterilir, ama şema düzeyinde her static prop aynı Prop
60
+ şeklini kabul eder).
61
+
62
+ ## Dil değiştirme: `language-selector`
63
+
64
+ Son kullanıcı dili `language-selector` komponentiyle değiştirir (`platform_guide('component')`
65
+ + `componentCatalog` içindeki `language-selector` kaydı): `change` olayı `machine.setLanguage(value)`
66
+ çağırır, seçim `localStorage`'a (`zeyrek:{appSlug}:lang`) yazılır ve çeviriye bağlı **bütün**
67
+ prop'lar sayfa yenilenmeden yeniden çözülür — ayrı bir akış kurmana gerek yok, komponenti sayfaya
68
+ eklemen yeterli. Projede dil tanımlı değilse (`languages` boşsa) liste boş çizilir.
69
+
70
+ ## Bilinen sınırlar
71
+
72
+ - **Flow adımlarında Translation kaynağı YOK.** `value.type` sözlüğünde (`platform_guide('page')`
73
+ Kural 1c sonundaki tablo) `translation` yoktur — bir flow adımının **değerini** doğrudan bir
74
+ çeviri anahtarına bağlayamazsın, yalnız sayfa şemasındaki statik prop'lar bağlanabilir. İfadeden
75
+ okumak gerekiyorsa `t('anahtar')` Jexl fonksiyonu kullanılır (`platform_guide('expressions')`).
76
+ - **Runtime sayfa yanıtı sunucu tarafında önbelleklidir** (Postgres `caches` tablosu). Bir
77
+ `translation_update`/`project_update(languages)` çağrısından hemen sonra `page_runtime_get`
78
+ eski değerleri gösterebilir — önbellek süresi dolana kadar. Acil doğrulama gerekiyorsa
79
+ `translation_list` ile yazma tarafını, `page_runtime_get` ile okuma tarafını AYRI doğrula.
80
+ - Mobil runtime (`apps/mobile`) bu özelliği henüz beslemiyor — yalnız web.
package/dist/server.js CHANGED
@@ -11,6 +11,7 @@ import { registerTemplateTools } from './tools/templates.js';
11
11
  import { registerWorkflowTools } from './tools/workflows.js';
12
12
  import { registerLayoutTools } from './tools/layouts.js';
13
13
  import { registerAccessTools } from './tools/access.js';
14
+ import { registerTranslationTools } from './tools/translations.js';
14
15
  import { registerFlowTools } from './tools/flows.js';
15
16
  import { registerKnowledgeResources } from './resources/knowledge.js';
16
17
  import { registerSchemaResources } from './resources/schemas.js';
@@ -47,6 +48,7 @@ const moduleRegistrations = {
47
48
  folder: registerFolderTools,
48
49
  file: registerFileTools,
49
50
  access: registerAccessTools,
51
+ translation: registerTranslationTools,
50
52
  template: registerTemplateTools,
51
53
  organization: registerOrganizationTools,
52
54
  dependency: registerDependencyTools,
@@ -13,7 +13,7 @@ export function registerPlatformTools(server, platform) {
13
13
  }, handle(config, () => platform.org('GET', '/connect/userinfo')));
14
14
  server.registerTool('platform_guide', {
15
15
  title: 'Platform rehberi oku',
16
- description: "Modülün iş kuralları rehberini döner (workflow, microservice, entity, page, component, html, middleware, view, expressions, getting-started, modify-existing). Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun; sayfa/layout/flow işleri için page, komponent modeli/olay sözleşmeleri için component, hazır komponentler yetmediğinde HTML kaçış kapısı (Mustache, fireEvent, script kuralları) için html, auth/oturum/yetki için middleware, tam metin arama indeksi için view, ifade yazarken kullanılabilir fonksiyon/bağlam sözlüğü (system.*, file.*, Page.*, state.*, Steps…) için expressions rehberi. Örnek gövdeler için ngapps://examples kaynaklarına bakın.",
16
+ description: "Modülün iş kuralları rehberini döner (workflow, microservice, entity, page, component, html, middleware, view, expressions, translation, getting-started, modify-existing). Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun; sayfa/layout/flow işleri için page, komponent modeli/olay sözleşmeleri için component, hazır komponentler yetmediğinde HTML kaçış kapısı (Mustache, fireEvent, script kuralları) için html, auth/oturum/yetki için middleware, tam metin arama indeksi için view, ifade yazarken kullanılabilir fonksiyon/bağlam sözlüğü (system.*, file.*, Page.*, state.*, Steps…) için expressions, sayfa/içerik çevirileri (çoklu dil) için translation rehberi. Örnek gövdeler için ngapps://examples kaynaklarına bakın.",
17
17
  inputSchema: {
18
18
  guide: z.string().describe('Rehber adı; bilinmeyen ad verilirse mevcut rehberler listelenir')
19
19
  },
@@ -79,12 +79,14 @@ export function registerProjectTools(server, platform) {
79
79
  }));
80
80
  server.registerTool('project_update', {
81
81
  title: 'Proje güncelle',
82
- description: 'Proje adını, slug\'ını ve temasını günceller. LoaderImage bu tool\'da desteklenmez.',
82
+ description: 'Proje adını, slug\'ını, temasını ve dil ayarlarını günceller. languages/defaultLanguage verilmezse mevcut değerlerine dokunulmaz. LoaderImage bu tool\'da desteklenmez.',
83
83
  inputSchema: {
84
84
  id: guid,
85
85
  name: z.string().min(1),
86
86
  slug: z.string().optional(),
87
- theme: z.string().optional()
87
+ theme: z.string().optional(),
88
+ languages: z.array(z.string().min(1)).min(1).optional().describe('ISO 639-1 kod listesi (proje dilleri); sayfa çevirileri bu kodlarla doğrulanır'),
89
+ defaultLanguage: z.string().min(1).optional().describe("languages içinde olmalı — proje dilleri hiç kurulmadıysa (ilk kurulumda) tek başına verilebilir")
88
90
  },
89
91
  annotations: updates
90
92
  }, handle(config, args => {
@@ -93,6 +95,9 @@ export function registerProjectTools(server, platform) {
93
95
  form.set('Slug', args.slug);
94
96
  if (args.theme)
95
97
  form.set('Theme', args.theme);
98
+ args.languages?.forEach(language => form.append('Languages', language));
99
+ if (args.defaultLanguage)
100
+ form.set('DefaultLanguage', args.defaultLanguage);
96
101
  return platform.org('PUT', `/projects/${args.id}`, { form });
97
102
  }));
98
103
  server.registerTool('project_delete', {
@@ -0,0 +1,37 @@
1
+ import { guid, projectParam, environmentParam, readOnly, updates } from './common.js';
2
+ import { handle } from '../platform/responses.js';
3
+ import { z } from 'zod';
4
+ const translationKeyInput = z.object({
5
+ key: z.string().min(1),
6
+ values: z.record(z.string()).describe("Dil kodu (ISO 639-1) → çeviri metni; kodlar projenin languages listesinde (project_get/project_update) tanımlı olmalı"),
7
+ componentId: guid.optional().describe("Çeviriyi belirli bir komponente bağlar; o komponent silinince kaydı da silinir")
8
+ });
9
+ export function registerTranslationTools(server, platform) {
10
+ const config = platform.config;
11
+ server.registerTool('translation_list', {
12
+ title: 'Çevirileri listele',
13
+ description: "Application'ın tüm çeviri kayıtlarını (dil × anahtar × değer) düz liste olarak döner. Proje dilleri/varsayılan dil için project_get; sayfada bir prop'u çeviriye bağlamak için platform_guide('translation').",
14
+ inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
15
+ annotations: readOnly
16
+ }, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, `/translations/${applicationId}`, { environment })));
17
+ server.registerTool('translation_update', {
18
+ title: 'Çevirileri güncelle',
19
+ description: "Bir ya da daha çok çeviri anahtarını toplu ekler/günceller (keys) ve/veya siler (deletedKeys) — tek çağrıda ikisi de verilebilir. 'keys' üzerinden yeni bir anahtar da açılabilir (upsert). 'values'taki dil kodları projenin languages listesinde tanımlı olmalı, aksi hâlde platform reddeder.",
20
+ inputSchema: {
21
+ project: projectParam,
22
+ environment: environmentParam,
23
+ applicationId: guid,
24
+ keys: z.array(translationKeyInput).optional().describe('Eklenecek/güncellenecek anahtarlar'),
25
+ deletedKeys: z.array(z.string().min(1)).optional().describe('Silinecek anahtar adları')
26
+ },
27
+ annotations: updates
28
+ }, handle(config, ({ project, environment, applicationId, keys, deletedKeys }) => {
29
+ if (!keys?.length && !deletedKeys?.length) {
30
+ throw new Error("'keys' ya da 'deletedKeys' parametrelerinden en az biri dolu olmalı.");
31
+ }
32
+ return platform.tenant('PUT', project, `/translations/${applicationId}`, {
33
+ environment,
34
+ body: { keys: keys ?? [], deletedKeys: deletedKeys ?? [] }
35
+ });
36
+ }));
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "MCP gateway exposing the NgApps platform APIs as tools",
6
6
  "license": "UNLICENSED",