@appaflytech/zeyrek-ai-mcp 0.0.18 → 0.0.20

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.
Files changed (65) hide show
  1. package/README.md +13 -14
  2. package/dist/config.js +2 -0
  3. package/dist/knowledge/componentCatalog.js +204 -836
  4. package/dist/knowledge/componentEvents.js +75 -368
  5. package/dist/knowledge/mobileComponentCatalog.js +331 -0
  6. package/dist/knowledge/mobileComponentEvents.js +50 -0
  7. package/dist/knowledge/normalize.js +63 -66
  8. package/dist/knowledge/platform/lint.js +205 -0
  9. package/dist/knowledge/platform/mobile.js +78 -0
  10. package/dist/knowledge/platform/mobileRules.js +121 -0
  11. package/dist/knowledge/platform/mobileTabs.js +108 -0
  12. package/dist/knowledge/platform/profile.js +20 -0
  13. package/dist/knowledge/platform/resolve.js +122 -0
  14. package/dist/knowledge/platform/web.js +38 -0
  15. package/dist/knowledge/rules.js +6 -29
  16. package/dist/knowledge/styleResolve.js +1 -1
  17. package/dist/knowledge/styleResolveNative.js +410 -0
  18. package/dist/knowledge/themeResolve.js +3 -3
  19. package/dist/platform/capture/actions.js +333 -0
  20. package/dist/platform/capture/browser.js +1 -0
  21. package/dist/platform/{screenshot.js → capture/capture.js} +23 -509
  22. package/dist/platform/capture/index.js +10 -0
  23. package/dist/platform/capture/readiness.js +103 -0
  24. package/dist/platform/capture/summary.js +77 -0
  25. package/dist/platform/capture/target.js +5 -0
  26. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  27. package/dist/platform/capture/targets/studio-web.js +5 -0
  28. package/dist/platform/domSummary.js +1 -1
  29. package/dist/platform/responses.js +1 -2
  30. package/dist/platform/strictArguments.js +2 -22
  31. package/dist/platform/writeDiff.js +31 -58
  32. package/dist/resources/examples/mobile-app-shell-drawer.json +209 -0
  33. package/dist/resources/examples/mobile-app-shell-stack.json +123 -0
  34. package/dist/resources/examples/mobile-app-shell-tab.json +174 -0
  35. package/dist/resources/examples/mobile-list-detail-form.json +656 -0
  36. package/dist/resources/guides/component.md +6 -9
  37. package/dist/resources/guides/functions.md +3 -32
  38. package/dist/resources/guides/getting-started.md +0 -35
  39. package/dist/resources/guides/layout.md +11 -7
  40. package/dist/resources/guides/mobile.md +215 -0
  41. package/dist/resources/guides/page.md +71 -49
  42. package/dist/resources/guides/translation.md +6 -5
  43. package/dist/resources/schema-fingerprint.json +2 -2
  44. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +0 -18
  45. package/dist/server.js +2 -0
  46. package/dist/tools/applications.js +60 -3
  47. package/dist/tools/components.js +50 -10
  48. package/dist/tools/designSchemaPatch.js +18 -13
  49. package/dist/tools/externalComponents.js +25 -4
  50. package/dist/tools/flows.js +51 -38
  51. package/dist/tools/functions.js +10 -20
  52. package/dist/tools/layouts.js +41 -9
  53. package/dist/tools/microservices.js +11 -39
  54. package/dist/tools/mobile.js +203 -0
  55. package/dist/tools/pages.js +127 -95
  56. package/dist/tools/platformInfo.js +2 -1
  57. package/dist/tools/profileWrite.js +45 -0
  58. package/dist/tools/schemaOps.js +2 -2
  59. package/dist/tools/screenWrite.js +2 -2
  60. package/dist/tools/screens.js +9 -3
  61. package/dist/tools/workflows.js +12 -39
  62. package/package.json +2 -7
  63. package/dist/platform/diagnostics.js +0 -85
  64. package/dist/platform/ifExists.js +0 -129
  65. package/dist/platform/writeView.js +0 -17
@@ -175,10 +175,11 @@ motor olmasıdır (tarayıcı JS'i ve Jint).
175
175
  verilmişse sekme başlığı çeviri sözlüğünden çözülür; anahtar boşsa `browserTitle` olduğu gibi
176
176
  kullanılır. İkisi de boşsa sayfa adı yazılır.
177
177
 
178
- > **Sekme başlığı canlı dil değişimini İZLER.** `language-selector` ile dil değiştirildiğinde
179
- > sayfa içeriğiyle birlikte sekme başlığı da yeni dilde çözülür; yeniden yükleme gerekmez
180
- > (ölçüldü 2026-09-11). Anahtarın seçilen dilde değeri yoksa projenin varsayılan diline düşer.
181
- > 2026-09-08 ölçümündeki "açılıştaki dilde kalır" davranışı düzeltildi.
178
+ > **Sekme başlığı SAYFA YÜKLENİRKEN çözülür, canlı dil değişimini İZLEMEZ.** Ölçüldü 2026-09-08
179
+ > (yerel runtime): `language-selector` ile dil değiştirildiğinde sayfa içeriği ve layout başlığı
180
+ > anında yeni dile geçiyor, sekme başlığı ise açılıştaki dilde kalıyor; aynı dille sayfa yeniden
181
+ > yüklendiğinde başlık doğru geliyor. Sebep `Runtime.svelte`'te dilin bir `const` olarak bir kez
182
+ > çözülmesi. Sekme başlığının dilini doğrulayacaksan **yeniden yükleyerek** doğrula.
182
183
 
183
184
  ## Doğrulama mesajları: `t:` öneki
184
185
 
@@ -208,7 +209,7 @@ gerekmez — `schema.ts`'e dokunulmaz, katalog yeniden yüklenmez.
208
209
  `translation_update` artık uygulamanın bütün sayfalarının önbellek satırlarını düşürüyor
209
210
  (`RefreshPageCacheWhenTranslationChangedCommand`), okuma yolu cache-aside olduğu için bir
210
211
  sonraki `page_runtime_get` taze değeri kurar. Yazmadan hemen sonra okuyabilirsin.
211
- - Mobil runtime (`apps/mobile`) bu özelliği henüz beslemiyor — yalnız web.
212
+ - Mobil uygulama runtime'ı (`@ng-apps/ui-native`) web ile aynı akış motorunu ve dil seçimini kullanır; cihazda ölçülene kadar kanıtlı olan web'dir.
212
213
 
213
214
  ## Backend doğrulama sınırları (TR-001)
214
215
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "hash": "f37265d598ea468deb2157165891629c2e88c7fd2238e82ec60cda7f58f7de4c",
3
- "generatedAt": "2026-09-18T12:57:15.416Z",
2
+ "hash": "05c7864d58e308973805f7becee1578ce9eddad196abcdf7eebb43b1e7677549",
3
+ "generatedAt": "2026-09-19T20:19:15.052Z",
4
4
  "types": 186,
5
5
  "files": 108,
6
6
  "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
@@ -95,20 +95,6 @@
95
95
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Shared.Dependencies.DependencyNode"
96
96
  },
97
97
  "nullable": true
98
- },
99
- "bindings": {
100
- "type": "array",
101
- "items": {
102
- "$ref": "#/components/schemas/NgApps.Domain.Contexts.Shared.Dependencies.DependencyNode"
103
- },
104
- "nullable": true
105
- },
106
- "functions": {
107
- "type": "array",
108
- "items": {
109
- "$ref": "#/components/schemas/NgApps.Domain.Contexts.Shared.Dependencies.DependencyNode"
110
- },
111
- "nullable": true
112
98
  }
113
99
  },
114
100
  "additionalProperties": false
@@ -203,10 +189,6 @@
203
189
  },
204
190
  "stepKind": {
205
191
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Studio.Flows.StepKind"
206
- },
207
- "propertyName": {
208
- "type": "string",
209
- "nullable": true
210
192
  }
211
193
  },
212
194
  "additionalProperties": false
package/dist/server.js CHANGED
@@ -39,6 +39,7 @@ import { registerMergeTools } from "./tools/merges.js";
39
39
  import { registerRevisionTools } from "./tools/revisions.js";
40
40
  import { registerActivityTools } from "./tools/activities.js";
41
41
  import { registerDtoTools } from "./tools/dtos.js";
42
+ import { registerMobileTools } from "./tools/mobile.js";
42
43
  const moduleRegistrations = {
43
44
  platform: registerPlatformTools,
44
45
  project: registerProjectTools,
@@ -80,6 +81,7 @@ const moduleRegistrations = {
80
81
  merge: registerMergeTools,
81
82
  activity: registerActivityTools,
82
83
  revision: registerRevisionTools,
84
+ mobile: registerMobileTools,
83
85
  };
84
86
  export function createGatewayServer(platform) {
85
87
  const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config));
@@ -1,6 +1,44 @@
1
1
  import { guid, pagingParams, readOnly, writes, updates, destructive } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
+ import { forgetApplication, readApplicationPlatform, resolveApplication } from '../knowledge/platform/resolve.js';
4
+ import { mobileTabsProblems, readMobileTabs } from '../knowledge/platform/mobileTabs.js';
5
+ import { findingLine } from '../knowledge/platform/profile.js';
3
6
  import { z } from 'zod';
7
+ /**
8
+ * MOB-007 (sekme ayarı) — `properties.mobile.tabs` yazan mobil uygulama çağrısının denetimi. Yazım
9
+ * platforma YİNE gider (D3; backend `properties`'i doğrulamaz): bulgu `platformWarnings`'e düşer.
10
+ * Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar.
11
+ */
12
+ export async function mobileTabsWarnings(platform, applicationId, projectSlug, homepageId, properties) {
13
+ const tabs = readMobileTabs(properties);
14
+ if (tabs === undefined)
15
+ return [];
16
+ const pages = applicationId && projectSlug
17
+ ? await platform.tenant('GET', projectSlug, `/pages/${applicationId}`, { query: { pageIndex: 0, pageLength: 200 } })
18
+ .then(response => (response?.data ?? []).map(page => ({ id: String(page.id ?? '') })))
19
+ .catch(() => undefined)
20
+ : undefined;
21
+ const widgetIds = applicationId && projectSlug
22
+ ? await platform.tenant('GET', projectSlug, `/components/${applicationId}`, { query: { pageIndex: 0, pageLength: 999 } })
23
+ .then(response => new Set((response?.data ?? []).filter(entry => entry.kind === 'widget').map(entry => String(entry.id ?? ''))))
24
+ .catch(() => undefined)
25
+ : undefined;
26
+ return mobileTabsProblems(tabs, {
27
+ ...(pages ? { pages } : {}),
28
+ ...(homepageId ? { homepageId } : {}),
29
+ ...(widgetIds ? { widgetIds } : {})
30
+ }).map(message => findingLine({
31
+ code: 'MOB-007',
32
+ severity: 'warn',
33
+ message,
34
+ guide: "platform_guide('mobile') §Gezinme"
35
+ }));
36
+ }
37
+ function withPlatformWarnings(response, warnings) {
38
+ if (warnings.length === 0 || !response || typeof response !== 'object' || Array.isArray(response))
39
+ return response;
40
+ return { ...response, platformWarnings: warnings };
41
+ }
4
42
  export function registerApplicationTools(server, platform) {
5
43
  const config = platform.config;
6
44
  server.registerTool('application_list', {
@@ -34,7 +72,14 @@ export function registerApplicationTools(server, platform) {
34
72
  notFoundPageId: guid.optional()
35
73
  },
36
74
  annotations: writes
37
- }, handle(config, ({ projectId, ...body }) => platform.org('POST', `/applications/${projectId}`, { body })));
75
+ }, handle(config, async ({ projectId, ...body }) => {
76
+ const warnings = readApplicationPlatform(body.type) === 'mobile'
77
+ ? await mobileTabsWarnings(platform, undefined, undefined, undefined, body.properties)
78
+ : [];
79
+ const response = await platform.org('POST', `/applications/${projectId}`, { body });
80
+ forgetApplication(platform);
81
+ return withPlatformWarnings(response, warnings);
82
+ }));
38
83
  server.registerTool('application_update', {
39
84
  title: 'Application güncelle',
40
85
  description: 'Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın).',
@@ -49,11 +94,23 @@ export function registerApplicationTools(server, platform) {
49
94
  notFoundPageId: guid.optional()
50
95
  },
51
96
  annotations: updates
52
- }, handle(config, ({ id, ...body }) => platform.org('PUT', `/applications/${id}`, { body })));
97
+ }, handle(config, async ({ id, ...body }) => {
98
+ const current = await resolveApplication(platform, id);
99
+ const warnings = current?.platform === 'mobile'
100
+ ? await mobileTabsWarnings(platform, id, current.projectSlug, body.homepageId, body.properties)
101
+ : [];
102
+ const response = await platform.org('PUT', `/applications/${id}`, { body });
103
+ forgetApplication(platform, id);
104
+ return withPlatformWarnings(response, warnings);
105
+ }));
53
106
  server.registerTool('application_delete', {
54
107
  title: 'Application sil',
55
108
  description: 'Application\'ı kalıcı olarak siler.',
56
109
  inputSchema: { id: guid },
57
110
  annotations: destructive
58
- }, handle(config, args => platform.org('DELETE', `/applications/${args.id}`)));
111
+ }, handle(config, async (args) => {
112
+ const response = await platform.org('DELETE', `/applications/${args.id}`);
113
+ forgetApplication(platform, args.id);
114
+ return response;
115
+ }));
59
116
  }
@@ -1,25 +1,40 @@
1
1
  import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
2
- import { normalizeComponentDefaults, componentDefaultsNote, clampBlockSizes, blockSizeNote, normalizeHeightTypes, heightTypeNote, normalizeSizeStrings, sizeStringNote, withNormalization } from '../knowledge/normalize.js';
2
+ import { normalizeComponentDefaults, componentDefaultsNote, clampBlockSizes, blockSizeNote, normalizeHeightTypes, heightTypeNote, normalizeSizeStrings, sizeStringNote, normalizeDroppableStates, withNormalization } from '../knowledge/normalize.js';
3
3
  import { eventContractsFor, eventPayloadNote } from '../knowledge/componentEvents.js';
4
4
  import { componentPresetDefaults, readProjectTheme } from '../knowledge/themeSchema.js';
5
5
  import { knowledgeFor, knowledgeNote, APPEARANCE_TOKEN_SETS, STATE_FLAG_NOTES, INTERNAL_ROLES_BY_TYPE } from '../knowledge/componentCatalog.js';
6
+ import { MOBILE_STATE_FLAG_NOTES, mobileComponentKnowledge } from '../knowledge/mobileComponentCatalog.js';
7
+ import { mobileComponentEventContracts } from '../knowledge/mobileComponentEvents.js';
6
8
  import { componentUpdateBody, fetchHost, schemaParam, unwrap } from './screens.js';
7
9
  import { PlatformApiError } from '../platform/errors.js';
8
10
  import { handle } from '../platform/responses.js';
11
+ import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
12
+ import { mobileComponentNotes } from '../knowledge/platform/mobileRules.js';
9
13
  import { z } from 'zod';
10
- const typeParam = z.enum(['web', 'mobile']).optional().describe('Hedef platform; varsayılan web');
14
+ const typeParam = z.enum(['web', 'mobile']).optional().describe('Hedef platform (ComponentType); verilmezse uygulamanın tipi — mobil uygulamada mobile');
11
15
  const kindParam = z.enum(['component', 'widget']).optional()
12
16
  .describe("component: sayfaya konan tekil eleman. widget: kendi şeması olan kapsayıcı (container/modal/drawer)");
13
17
  const widgetTypeParam = z.enum(['container', 'modal', 'drawer']).optional()
14
18
  .describe("kind:'widget' iken zorunlu");
19
+ /**
20
+ * Uç `type` verilmezse WEB kataloğunu döner (`GetComponentsQuery` `Type ?? Web`); mobil uygulamanın
21
+ * paleti, doğrulayıcısı ve widget'ları `type=mobile` satırlarıdır. Profil mobil derse sorgu tipi
22
+ * taşır; web'de sorgu profil öncesiyle aynıdır (G1 kilidi).
23
+ */
15
24
  async function listComponents(platform, project, environment, applicationId, scope) {
16
25
  const path = scope === 'all' ? `/components/${applicationId}/all` : `/components/${applicationId}`;
26
+ const catalogType = scope === 'all' ? 'web' : (await resolveProfile(platform, applicationId)).profile.catalogType;
17
27
  const response = await platform.tenant('GET', project, path, {
18
28
  environment,
19
- query: scope === 'all' ? undefined : { pageIndex: 0, pageLength: 999 }
29
+ query: scope === 'all' ? undefined : { pageIndex: 0, pageLength: 999, ...(catalogType === 'mobile' ? { type: 'mobile' } : {}) }
20
30
  });
21
31
  return response?.data ?? [];
22
32
  }
33
+ /** Uygulamanın kayıtlı bileşen slug'ları (mobil profilin MOB-003 denetimi). */
34
+ export async function fetchCatalogSlugs(platform, project, environment, applicationId) {
35
+ const components = await listComponents(platform, project, environment, applicationId);
36
+ return new Set(components.map(component => String(component.slug ?? '')).filter(slug => slug.length > 0));
37
+ }
23
38
  /**
24
39
  * Yerleşim normalizasyonlarının ikisi de aynı katalog listesinden beslenir; tek okumada
25
40
  * çıkarılır ki yazma yolları fazladan istek atmasın. Uygulamanın kendi sözlüğü okunur
@@ -38,12 +53,17 @@ export async function fetchComponentLayoutRules(platform, project, environment,
38
53
  export function componentLayoutRules(components) {
39
54
  const maxSizes = new Map();
40
55
  const autoHeights = new Map();
56
+ const droppable = new Map();
41
57
  for (const component of components) {
42
58
  const slug = typeof component.slug === 'string' ? component.slug : undefined;
43
59
  if (!slug)
44
60
  continue;
45
61
  if (typeof component.isDefaultAutoHeight === 'boolean')
46
62
  autoHeights.set(slug, component.isDefaultAutoHeight);
63
+ // Kap bilgisi sözlükten gelir; düğümün `states.isDroppable`ı onu izler (Studio paleti de
64
+ // aynı alanı katalogdan yazar). `true` gören tip kap sayılır.
65
+ if (typeof component.isDroppable === 'boolean')
66
+ droppable.set(slug, component.isDroppable);
47
67
  const blocks = (component.definition ?? {}).blocks;
48
68
  if (!Array.isArray(blocks))
49
69
  continue;
@@ -58,7 +78,7 @@ export function componentLayoutRules(components) {
58
78
  });
59
79
  }
60
80
  }
61
- return { maxSizes, autoHeights };
81
+ return { maxSizes, autoHeights, droppable };
62
82
  }
63
83
  /**
64
84
  * Katalog okuması başarısız olsa da yazma isteği gitmeli, ama iki normalizasyonun düşüş davranışı
@@ -69,14 +89,21 @@ export function componentLayoutRules(components) {
69
89
  * varsayılanını (`fixed`, `DesignComponentManagementSchema.cs:24`) kabul etmek demektir; en fazla
70
90
  * sekiz tipte (auth-layout/html/image/layout/qrcode/table/text-editor/video) fazladan yükseklik
71
91
  * üretir, atlamak ise her tipte içerik kırpar.
92
+ * - `states.isDroppable` atlanır — katalog okunamadığında hangi tipin kap olduğu bilinmez ve
93
+ * uydurulmaz; düğüm bugünkü hâliyle yazılır.
72
94
  */
73
95
  export async function applyCatalogLayoutRules(platform, project, environment, applicationId, nodes) {
74
96
  try {
75
97
  const rules = await fetchComponentLayoutRules(platform, project, environment, applicationId);
76
- return { clamped: clampBlockSizes(nodes, rules.maxSizes), heights: normalizeHeightTypes(nodes, rules.autoHeights), sizes: normalizeSizeStrings(nodes) };
98
+ return {
99
+ clamped: clampBlockSizes(nodes, rules.maxSizes),
100
+ heights: normalizeHeightTypes(nodes, rules.autoHeights),
101
+ sizes: normalizeSizeStrings(nodes),
102
+ states: normalizeDroppableStates(nodes, rules.droppable)
103
+ };
77
104
  }
78
105
  catch {
79
- return { clamped: [], heights: normalizeHeightTypes(nodes, new Map()), sizes: normalizeSizeStrings(nodes) };
106
+ return { clamped: [], heights: normalizeHeightTypes(nodes, new Map()), sizes: normalizeSizeStrings(nodes), states: [] };
80
107
  }
81
108
  }
82
109
  /**
@@ -116,6 +143,7 @@ export function registerComponentTools(server, platform) {
116
143
  }, handle(config, async ({ project, environment, applicationId, scope, includeHidden }) => {
117
144
  const components = await listComponents(platform, project, environment, applicationId, scope);
118
145
  return {
146
+ ...applicationTypeField(await resolveProfile(platform, applicationId)),
119
147
  components: components
120
148
  .filter(component => includeHidden === true || component.isHide !== true)
121
149
  .map(component => ({
@@ -161,6 +189,15 @@ export function registerComponentTools(server, platform) {
161
189
  : [];
162
190
  const componentSlug = typeof component.slug === 'string' ? component.slug : undefined;
163
191
  const knowledge = knowledgeFor(componentSlug);
192
+ // Mobil uygulamada kayıt mobil kataloğun satırıdır (`listComponents` type=mobile); davranış
193
+ // notları web runtime'ından gelir (`knowledge` — mobil şema web sınıfını genişletir), bileşenin
194
+ // mobil farkları `mobileKnowledge`'ta, bütün mobil bileşenlere geçen notlar `mobileNotes`'ta (G4).
195
+ // Web ikizi olmayan bileşende (`app-bar`, `tooltip`…) bilgi yalnız `mobileKnowledge`'tadır; olay
196
+ // yükleri de önce mobil sözlükten okunur (`mobileComponentEvents.ts`).
197
+ const mobile = (await resolveProfile(platform, applicationId)).profile.platform === 'mobile';
198
+ const mobileKnowledge = mobile && componentSlug !== undefined && Object.hasOwn(mobileComponentKnowledge, componentSlug)
199
+ ? mobileComponentKnowledge[componentSlug]
200
+ : undefined;
164
201
  // S-3 — `defaults` HAM katalogdur; Studio'da bırakılan düğüm ise temanın bileşen preseti
165
202
  // kaplanmış katalogdan kurulur (`withThemeComponentDefaults`). Tema okunamazsa (uç düşer,
166
203
  // yetki yoksa) alan yanıta HİÇ girmez: uydurulmuş bir varsayılan yanlış bilgidir.
@@ -177,7 +214,7 @@ export function registerComponentTools(server, platform) {
177
214
  isDroppable: component.isDroppable,
178
215
  props: definition.props,
179
216
  defaults: definition.defaults,
180
- events: eventContractsFor(componentSlug, eventNames),
217
+ events: eventContractsFor(componentSlug, eventNames, mobile ? mobileComponentEventContracts : undefined),
181
218
  eventPayloadNote,
182
219
  ...(knowledge
183
220
  ? {
@@ -188,7 +225,7 @@ export function registerComponentTools(server, platform) {
188
225
  },
189
226
  ...(knowledge.guide ? { guide: knowledge.guide } : {})
190
227
  }
191
- : { knowledgeNote }),
228
+ : mobileKnowledge ? {} : { knowledgeNote }),
192
229
  ...(componentSlug && INTERNAL_ROLES_BY_TYPE[componentSlug]
193
230
  ? {
194
231
  internalRoles: INTERNAL_ROLES_BY_TYPE[componentSlug],
@@ -196,8 +233,9 @@ export function registerComponentTools(server, platform) {
196
233
  }
197
234
  : {}),
198
235
  ...(themeDefaults ? { themeDefaults } : {}),
236
+ ...(mobile ? { platform: 'mobile', ...(mobileKnowledge ? { mobileKnowledge } : {}), mobileNotes: mobileComponentNotes(componentSlug) } : {}),
199
237
  appearanceValues: APPEARANCE_TOKEN_SETS,
200
- stateFlags: STATE_FLAG_NOTES,
238
+ stateFlags: mobile ? { ...STATE_FLAG_NOTES, ...MOBILE_STATE_FLAG_NOTES } : STATE_FLAG_NOTES,
201
239
  validations: definition.validations,
202
240
  sizes: definition.blocks
203
241
  };
@@ -234,11 +272,13 @@ export function registerComponentTools(server, platform) {
234
272
  const catalog = (body.schema ?? []).length > 0
235
273
  ? await applyCatalogLayoutRules(platform, project, environment, applicationId, body.schema)
236
274
  : { clamped: [], heights: [], sizes: [] };
275
+ // Mobil uygulamanın widget'ı mobil katalog satırıdır; tip verilmezse uygulamanın tipi.
276
+ const defaultType = body.type ?? (await resolveProfile(platform, applicationId)).profile.catalogType;
237
277
  return platform.tenant('POST', project, `/components/${applicationId}`, {
238
278
  environment,
239
279
  body: {
240
280
  kind: 'component',
241
- type: 'web',
281
+ type: defaultType,
242
282
  categories: [],
243
283
  definition: {},
244
284
  schema: [],
@@ -1,13 +1,15 @@
1
- import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, normalizeSchemaOps, validationNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
1
+ import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, staleSetterWarnings, normalizeSchemaOps, validationNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
2
2
  import { opsParam, writeViewParam, outlineWriteResponse, unknownEventOps, collectTypes, simulateLayoutOps, removeNodeFromTree, planEventRemovals, planResizes, planPropTypes, assertOpBodies } from './schemaOps.js';
3
3
  import { guid, projectParam, environmentParam, updates, microOps } from './common.js';
4
4
  import { fetchHost, hostPath } from './screens.js';
5
5
  import { assertScreenWriteGuards, screenCatalogNotes } from './screenWrite.js';
6
- import { emptyWriteDiff, schemaOpDrops, withWriteDiff } from '../platform/writeDiff.js';
6
+ import { schemaOpDrops, withWriteDiff } from '../platform/writeDiff.js';
7
7
  import { fetchComponentEventNames } from './components.js';
8
8
  import { PlatformApiError } from '../platform/errors.js';
9
9
  import { handle } from '../platform/responses.js';
10
10
  import { slotWarning } from './layouts.js';
11
+ import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
12
+ import { boundFlowSteps, screenProfileWarnings } from './profileWrite.js';
11
13
  import { z } from 'zod';
12
14
  const HOSTS = {
13
15
  layout: {
@@ -68,10 +70,7 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
68
70
  }
69
71
  const nodes = (record[spec.tree] ?? []);
70
72
  const events = host === 'layout' ? record.events : undefined;
71
- if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
72
- const bound = boundEventOps(nodes, opRecords);
73
- if (bound.length > 0)
74
- throw new PlatformApiError(`PAGE-034 — dolu olaya set_event (istek gönderilmedi): ${bound.join(' · ')}`, 400);
73
+ if (opRecords.some(entry => entry.op === 'set_event')) {
75
74
  const typesByElementId = new Map();
76
75
  collectTypes(nodes, typesByElementId);
77
76
  collectTypes(opRecords.map(entry => entry.component).filter(Boolean), typesByElementId);
@@ -80,7 +79,6 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
80
79
  if (unknown.length > 0)
81
80
  throw new PlatformApiError(`PAGE-008 — geçersiz olay adı (istek gönderilmedi): ${unknown.join(' · ')}`, 400);
82
81
  }
83
- stripGatewayEventFields(opRecords);
84
82
  const deletedFlows = orphanedFlowPlan(nodes, events, [...opRecords, ...removeEventOps]);
85
83
  if (deletedFlows.length > 0 && allowFlowDeletion !== true)
86
84
  throw new PlatformApiError(orphanedFlowMessage(deletedFlows), 400);
@@ -101,12 +99,18 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
101
99
  const nested = simulatable ? nestedRepeaterNodes(simulated) : [];
102
100
  if (nested.length > 0)
103
101
  throw new PlatformApiError(nestedRepeaterMessage(nested), 400);
102
+ const resolved = await resolveProfile(platform, applicationId);
104
103
  const reshaped = simulatable || opRecords.some(entry => entry.op === 'remove_component');
104
+ const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, {
105
+ kind: spec.kind,
106
+ ops: [...opRecords, ...propTypeOps],
107
+ steps: await boundFlowSteps(platform, resolved, project, environment, host, applicationId, id, opRecords)
108
+ });
105
109
  const layoutWarnings = reshaped
106
110
  ? [
107
- ...verticalGapWarnings(simulated, spec.codes.gap, touched),
111
+ ...(resolved.profile.layout.blocksMeaningful ? verticalGapWarnings(simulated, spec.codes.gap, touched) : []),
108
112
  ...stringStyleWarning(stringStyleNodes(simulated)),
109
- ...inertFlexPropWarning(inertFlexPropNodes(simulated)),
113
+ ...(resolved.profile.layout.modes.has('grid') ? inertFlexPropWarning(inertFlexPropNodes(simulated)) : []),
110
114
  ...flexSizeWarnings(simulated, spec.codes.flexMismatch, spec.codes.flexInvalid),
111
115
  ...(host === 'layout' ? slotWarning(simulated) : [])
112
116
  ]
@@ -133,17 +137,18 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
133
137
  response.deletedFlows = deletedFlows;
134
138
  if (flowWarnings.length > 0)
135
139
  response.flowWarnings = flowWarnings;
136
- if (layoutWarnings.length > 0)
137
- response.layoutWarnings = layoutWarnings;
140
+ if (layoutWarnings.length > 0 || profileWarnings.length > 0)
141
+ response.layoutWarnings = [...layoutWarnings, ...profileWarnings];
142
+ Object.assign(response, applicationTypeField(resolved));
138
143
  const contentOps = opRecords.filter(entry => entry.op === 'add_component' || entry.op === 'replace_prop');
139
- let opDrops = emptyWriteDiff();
144
+ let opDrops = [];
140
145
  if (contentOps.length > 0) {
141
146
  try {
142
147
  const saved = await fetchHost(platform, host, project, environment, applicationId, id);
143
148
  opDrops = schemaOpDrops(saved[spec.tree], contentOps);
144
149
  }
145
150
  catch {
146
- opDrops = emptyWriteDiff();
151
+ opDrops = [];
147
152
  }
148
153
  }
149
154
  return withWriteDiff(withNormalization(response, [
@@ -7,6 +7,23 @@ import { z } from 'zod';
7
7
  import { evaluatePolicy, enforcePolicy } from '../knowledge/policy.js';
8
8
  import { policyRules } from '../knowledge/rules.js';
9
9
  import { EXTERNAL_COMPONENT_PROTOCOL_VERSION } from '../knowledge/backendConstraints.js';
10
+ import { resolveProfile } from '../knowledge/platform/resolve.js';
11
+ import { findingLine } from '../knowledge/platform/profile.js';
12
+ /**
13
+ * MOB-006 — harici bileşen protokolü v1 tarayıcıda koşan `.mjs` custom element'tir; mobil
14
+ * uygulamanın native çizeri onu yükleyemez (mobil protokol v2 Faz 3). Profil kapalı derse çağrı
15
+ * platforma GİTMEZ: yüklenen paket hiçbir ekranda çizilmeyecekti.
16
+ */
17
+ async function assertExternalComponentsAllowed(platform, applicationId, tool) {
18
+ const resolved = await resolveProfile(platform, applicationId);
19
+ if (resolved.profile.externalComponents)
20
+ return;
21
+ throw new PlatformApiError(`${tool}: ${findingLine({
22
+ code: 'MOB-006',
23
+ severity: 'warn',
24
+ message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list)."
25
+ })}`, 400);
26
+ }
10
27
  /** Platformun köprü protokolü; scaffold ve publish bu sürüme göre üretir/doğrular (backend ExternalComponentRules.CurrentProtocolVersion) */
11
28
  const PROTOCOL_VERSION = EXTERNAL_COMPONENT_PROTOCOL_VERSION;
12
29
  /** Backend `ExternalComponentRules.MaxBundleSizeBytes` ile aynı sınır */
@@ -199,6 +216,7 @@ export function registerExternalComponentTools(server, platform) {
199
216
  },
200
217
  annotations: writes
201
218
  }, handle(config, async ({ project, environment, applicationId, bundlePath, manifestPath, version }) => {
219
+ await assertExternalComponentsAllowed(platform, applicationId, 'external_component_publish');
202
220
  const manifestFile = resolve(manifestPath);
203
221
  const bundleFile = resolve(bundlePath);
204
222
  let manifest;
@@ -262,10 +280,13 @@ export function registerExternalComponentTools(server, platform) {
262
280
  acknowledgeWarnings: z.boolean().optional().describe('Doğrulama uyarılarını okuyup kabul ettiğinizi bildirir; uyarısı olan sürümlerde zorunlu')
263
281
  },
264
282
  annotations: updates
265
- }, handle(config, ({ project, environment, applicationId, slug, version, acknowledgeWarnings }) => platform.tenant('POST', project, `/components/${applicationId}/external/${slug}/activate`, {
266
- environment,
267
- body: { version, acknowledgeWarnings: acknowledgeWarnings === true }
268
- })));
283
+ }, handle(config, async ({ project, environment, applicationId, slug, version, acknowledgeWarnings }) => {
284
+ await assertExternalComponentsAllowed(platform, applicationId, 'external_component_activate');
285
+ return platform.tenant('POST', project, `/components/${applicationId}/external/${slug}/activate`, {
286
+ environment,
287
+ body: { version, acknowledgeWarnings: acknowledgeWarnings === true }
288
+ });
289
+ }));
269
290
  server.registerTool('external_component_versions', {
270
291
  title: 'Paket sürümlerini listele',
271
292
  description: 'Bir harici komponentin yayınlanmış paket sürümlerini, hangisinin aktif olduğunu ve her sürümün doğrulama raporunu listeler. Geri alınacak sürümü buradan seçin.',