@appaflytech/zeyrek-ai-mcp 0.0.3 → 0.0.4

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 (35) hide show
  1. package/.env.example +21 -7
  2. package/.env.production.example +42 -0
  3. package/README.md +14 -7
  4. package/dist/config.js +113 -47
  5. package/dist/knowledge/componentCatalog.js +474 -31
  6. package/dist/knowledge/componentEvents.js +150 -12
  7. package/dist/knowledge/errorPath.js +64 -0
  8. package/dist/knowledge/normalize.js +12 -1
  9. package/dist/knowledge/outlines.js +6 -0
  10. package/dist/knowledge/rules.js +184 -3
  11. package/dist/knowledge/styleResolve.js +485 -0
  12. package/dist/observability/trace.js +4 -0
  13. package/dist/platform/http.js +3 -0
  14. package/dist/platform/responses.js +80 -4
  15. package/dist/platform/screenshot.js +106 -0
  16. package/dist/resources/examples/layout-app-shell.json +368 -0
  17. package/dist/resources/examples/microservice-table-list.json +1 -0
  18. package/dist/resources/examples/middleware-authenticate.json +243 -47
  19. package/dist/resources/examples/workflow-basic.json +24 -2
  20. package/dist/resources/guides/component.md +6 -3
  21. package/dist/resources/guides/external-component.md +129 -0
  22. package/dist/resources/guides/layout.md +147 -0
  23. package/dist/resources/guides/microservice.md +35 -0
  24. package/dist/resources/guides/middleware.md +10 -0
  25. package/dist/resources/guides/page.md +132 -14
  26. package/dist/resources/guides/workflow.md +127 -2
  27. package/dist/server.js +2 -0
  28. package/dist/tools/externalComponents.js +285 -0
  29. package/dist/tools/layouts.js +48 -6
  30. package/dist/tools/microservices.js +2 -2
  31. package/dist/tools/pages.js +98 -30
  32. package/dist/tools/platformInfo.js +2 -2
  33. package/dist/tools/screens.js +36 -0
  34. package/dist/tools/workflows.js +3 -3
  35. package/package.json +4 -1
@@ -0,0 +1,106 @@
1
+ import { PlatformApiError } from './errors.js';
2
+ import { logStep } from '../observability/trace.js';
3
+ const navigationTimeoutMs = 20000;
4
+ const apiResponseTimeoutMs = 15000;
5
+ const settleMs = 600;
6
+ const idleCloseMs = 60000;
7
+ export class StudioScreenshotter {
8
+ config;
9
+ launcher;
10
+ browserPromise;
11
+ idleTimer;
12
+ constructor(config, launcher) {
13
+ this.config = config;
14
+ this.launcher = launcher;
15
+ }
16
+ async launch() {
17
+ if (this.launcher)
18
+ return this.launcher();
19
+ let chromium;
20
+ try {
21
+ chromium = (await import('playwright-core')).chromium;
22
+ }
23
+ catch {
24
+ throw new PlatformApiError('playwright-core paketi yüklü değil — zeyrek-mcp dizininde `npm install playwright-core` çalıştırın, ardından tarayıcı için `npx playwright-core install chromium`.');
25
+ }
26
+ logStep('tarayıcı →', 'chromium başlatılıyor (ilk çağrı — sonraki çağrılar önbellekteki tarayıcıyı kullanır)');
27
+ try {
28
+ return await chromium.launch({
29
+ headless: true,
30
+ ...(this.config.screenshotBrowserPath ? { executablePath: this.config.screenshotBrowserPath } : {})
31
+ });
32
+ }
33
+ catch (firstError) {
34
+ try {
35
+ return await chromium.launch({ headless: true, channel: 'chrome' });
36
+ }
37
+ catch {
38
+ const message = firstError instanceof Error ? firstError.message : String(firstError);
39
+ throw new PlatformApiError(`Chromium başlatılamadı: ${message}. \`npx playwright-core install chromium\` ile indirin; alternatif olarak sistemdeki tarayıcının yolunu SCREENSHOT_BROWSER_PATH ile verin (alpine imajında \`apk add chromium\` + /usr/bin/chromium-browser).`);
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Bekleme stratejisi bilinçli olarak `networkidle` DEĞİL: Studio'nun kök layout'u
45
+ * office-lite.zeyrek.dev'den dış bir script yükler ve ağ hiç durulmayabilir. SSR kabuğu yalnız
46
+ * spinner bastığı için domcontentloaded da tek başına yetmez. Bunun yerine sayfanın kendi veri
47
+ * isteği (`/{app}/p/{slug}`) gözlenir; yanıt hiç gelmezse bu hata SAYILMAZ — renderWaitTimedOut
48
+ * işaretlenir ve kare yine çekilir, çünkü bozuk/boş sayfayı görmek de bir doğrulamadır. Veri
49
+ * yanıtından sonra fontlar + kısa yerleşme beklemesi Svelte hydrate'ine alan bırakır.
50
+ */
51
+ async capture(request) {
52
+ const startedAt = Date.now();
53
+ if (this.idleTimer)
54
+ clearTimeout(this.idleTimer);
55
+ this.browserPromise ??= this.launch();
56
+ let browser = await this.browserPromise.catch(error => {
57
+ this.browserPromise = undefined;
58
+ throw error;
59
+ });
60
+ let context;
61
+ try {
62
+ context = await browser.newContext({ viewport: request.viewport, deviceScaleFactor: 1 });
63
+ }
64
+ catch {
65
+ this.browserPromise = this.launch();
66
+ browser = await this.browserPromise.catch(error => {
67
+ this.browserPromise = undefined;
68
+ throw error;
69
+ });
70
+ context = await browser.newContext({ viewport: request.viewport, deviceScaleFactor: 1 });
71
+ }
72
+ logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · ANONİM (çerez YOK)'}`);
73
+ try {
74
+ if (request.token) {
75
+ await context.addCookies([{ name: 'NG_AccessToken', value: request.token, url: request.cookieUrl, httpOnly: false }]);
76
+ }
77
+ const page = await context.newPage();
78
+ const observedApiStatus = page
79
+ .waitForResponse(response => response.url().includes(request.apiPathMarker), { timeout: apiResponseTimeoutMs })
80
+ .then(response => response.status(), () => undefined);
81
+ try {
82
+ await page.goto(request.url, { waitUntil: 'domcontentloaded', timeout: navigationTimeoutMs });
83
+ }
84
+ catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ throw new PlatformApiError(`Studio'ya ulaşılamadı: ${request.url} — ${message}. STUDIO_BASE_URL doğru mu, arayüz ayakta mı? (yerelde zeyrek-frontend/apps/studio içinde \`pnpm dev\`)`);
87
+ }
88
+ const apiStatus = await observedApiStatus;
89
+ await page.evaluate('document.fonts.ready.then(() => true)').catch(() => undefined);
90
+ await page.waitForTimeout(settleMs + request.delayMs);
91
+ const png = await page.screenshot({ type: 'png', fullPage: request.fullPage });
92
+ const elapsedMs = Date.now() - startedAt;
93
+ logStep('tarayıcı ←', `PNG ${png.byteLength} B · ${elapsedMs} ms · sayfa API yanıtı ${apiStatus === undefined ? 'gözlenmedi' : `HTTP ${apiStatus}`}`);
94
+ return { png, apiStatus, renderWaitTimedOut: apiStatus === undefined, elapsedMs };
95
+ }
96
+ finally {
97
+ await context.close().catch(() => undefined);
98
+ this.idleTimer = setTimeout(() => {
99
+ const expired = this.browserPromise;
100
+ this.browserPromise = undefined;
101
+ expired?.then(instance => instance.close()).catch(() => undefined);
102
+ }, idleCloseMs);
103
+ this.idleTimer.unref?.();
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,368 @@
1
+ {
2
+ "_meta": {
3
+ "synthetic": true,
4
+ "note": "Uygulama kabuğu: yan menü + üst şerit + sayfa slot'u. Studio'nun yeni layout iskeletiyle (getDefaultLayout) birebir aynı ağaç: sarmalayıcı container kök + heightType:'fill'. layout-shell/layout-region DEĞİL, sıradan container'lar. layout_create'in views alanına olduğu gibi verilebilir.",
5
+ "pairsWith": "page-detail-profile (slot'a girecek sayfa iskeleti), page-sign-in (layout'suz ekran)",
6
+ "sourceRef": "platform_guide('layout') Kural 5a/5b; zeyrek-frontend apps/studio/src/features/layout/layout.functions.ts (getDefaultLayout), packages/ng-apps-screen-builder/src/flex-layout.ts (getFlexChildStyle), packages/ng-apps-ui/src/shared/render/render.functions.ts"
7
+ },
8
+ "_rules": {
9
+ "viewsNotSchema": "Layout'ta şema alanının adı 'views'tir; micro-op hattı yoktur, değişiklik layout_update ile tam gövde olarak yazılır (LAY-001).",
10
+ "singleSlot": "TEK slot. Sayfa şeması HER slot'a ayrı ayrı kopyalanır — iki slot sayfayı iki kez çizer ve elementId'leri tekrarlar (LAY-002 / LAY-006).",
11
+ "propValueField": "Statik prop'un değeri 'data' alanındadır ({ type:'static', ref:null, data }); 'ref' referans tipleri (flow/file) içindir. Ham değer yazarsanız gateway sarmalar ve normalizasyon notu döner.",
12
+ "slotRowIsOpenEnded": "Slot'un grid satırı açık uçludur (runtime 'y / auto' yazar): sayfa uzadıkça slot uzar. Slot'a sabit yükseklik vermeye çalışma.",
13
+ "positionComesFromBlocks": "Konum ve boyut blocks[]'ten gelir. layout-region'daki dock prop'u konum DEĞİL biçim belirler; bu örnekte region hiç kullanılmıyor.",
14
+ "gridStacksFlexDoesNot": "Bu örnek layout:'flex' — fill'in 'kalanı doldur' anlamı flex'te tanımlıdır (sütunda flex:1 1 auto, satırda align-self:stretch). Bedeli MOBİL: runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer, flex satırı telefonda yan yana kalır (375px'te ölçüldü: 49/310px). Telefonda alt alta düşmesi gereken kabuk için kökü layout:'grid' kur; o zaman yükseklik yine blocks satırlarından gelir.",
15
+ "fixedPxWidthIsFlexOnly": "Bu örnek flex olduğu için yan menüye appearance.widthPx:240 verilebilir; verilmezse genişlik kolon payıdır (12'de 2 ≈ 1440px tasarım genişliğinde 240px). Izgara kabında widthPx okunmaz.",
16
+ "navigationsStyleIsDead": "navigations'ın style prop'u hiçbir şey yapmaz (Navigations.svelte style prop'u kabul etmiyor) — menü satırı biçimlendirmeye çalışma.",
17
+ "verifyWithRuntime": "Layout'un kendi serve URL'i yok: gerçek çıktıyı layout'a bağlı bir sayfada page_runtime_get ile, slot kurgusunu page_state.layoutSlots ile doğrula.",
18
+ "wrapperRoot": "Kök, sayfanın page_container'ının yerleşim ikizidir: states.isWrapper:true (çerçevesiz, başlıksız, saydam) + blocks x0/w12. Sarmalayıcı olmayan bir kök kenarlıklı beyaz bir kart olarak çizilir.",
19
+ "fillHeight": "Yükseklik heightType:'fill' ile ifade edilir: kök ekranı doldurur (100vh), yan menü ve içerik sütunu kökün bıraktığını, slot da üst şeritten artanı alır. size.h yalnız kaba bir kılavuzdur — 'fixed' + tahmini h ile yükseklik kurmaya çalışma."
20
+ },
21
+ "layoutCreate": {
22
+ "name": "Uygulama kabuğu",
23
+ "views": [
24
+ {
25
+ "elementId": "shell_root",
26
+ "name": "shell_root",
27
+ "type": "container",
28
+ "kind": "component",
29
+ "props": {
30
+ "title": {
31
+ "type": "static",
32
+ "ref": null,
33
+ "data": ""
34
+ },
35
+ "layout": {
36
+ "type": "static",
37
+ "ref": null,
38
+ "data": "flex"
39
+ },
40
+ "direction": {
41
+ "type": "static",
42
+ "ref": null,
43
+ "data": "row"
44
+ },
45
+ "gap": {
46
+ "type": "static",
47
+ "ref": null,
48
+ "data": "none"
49
+ },
50
+ "verticalAlign": {
51
+ "type": "static",
52
+ "ref": null,
53
+ "data": "stretch"
54
+ }
55
+ },
56
+ "states": {
57
+ "isDroppable": true,
58
+ "isWrapper": true
59
+ },
60
+ "appearance": {
61
+ "heightType": "fill",
62
+ "marginType": "none",
63
+ "paddingType": "none"
64
+ },
65
+ "blocks": [
66
+ {
67
+ "breakpoint": 1024,
68
+ "position": {
69
+ "x": 0,
70
+ "y": 0
71
+ },
72
+ "size": {
73
+ "w": 12,
74
+ "h": 80
75
+ }
76
+ }
77
+ ],
78
+ "events": [],
79
+ "validations": [],
80
+ "stateBindings": [],
81
+ "children": [
82
+ {
83
+ "elementId": "shell_sidebar",
84
+ "name": "shell_sidebar",
85
+ "type": "container",
86
+ "kind": "component",
87
+ "props": {
88
+ "title": {
89
+ "type": "static",
90
+ "ref": null,
91
+ "data": ""
92
+ },
93
+ "layout": {
94
+ "type": "static",
95
+ "ref": null,
96
+ "data": "flex"
97
+ },
98
+ "direction": {
99
+ "type": "static",
100
+ "ref": null,
101
+ "data": "column"
102
+ },
103
+ "gap": {
104
+ "type": "static",
105
+ "ref": null,
106
+ "data": "none"
107
+ }
108
+ },
109
+ "states": {
110
+ "isDroppable": true
111
+ },
112
+ "appearance": {
113
+ "heightType": "fill",
114
+ "marginType": "none",
115
+ "paddingType": "none"
116
+ },
117
+ "blocks": [
118
+ {
119
+ "breakpoint": 1024,
120
+ "position": {
121
+ "x": 0,
122
+ "y": 0
123
+ },
124
+ "size": {
125
+ "w": 2,
126
+ "h": 80
127
+ }
128
+ }
129
+ ],
130
+ "events": [],
131
+ "validations": [],
132
+ "stateBindings": [],
133
+ "children": [
134
+ {
135
+ "elementId": "shell_menu",
136
+ "name": "shell_menu",
137
+ "type": "navigations",
138
+ "kind": "component",
139
+ "props": {
140
+ "navigations": {
141
+ "type": "static",
142
+ "ref": null,
143
+ "data": [
144
+ {
145
+ "href": "",
146
+ "title": "SATIŞ",
147
+ "children": [
148
+ {
149
+ "href": "panel",
150
+ "title": "Panel",
151
+ "icon": {
152
+ "name": "home"
153
+ }
154
+ },
155
+ {
156
+ "href": "sirketler",
157
+ "title": "Şirketler",
158
+ "icon": {
159
+ "name": "domain"
160
+ }
161
+ }
162
+ ]
163
+ }
164
+ ]
165
+ }
166
+ },
167
+ "states": {},
168
+ "appearance": {
169
+ "heightType": "auto",
170
+ "marginType": "none",
171
+ "paddingType": "none"
172
+ },
173
+ "blocks": [
174
+ {
175
+ "breakpoint": 1024,
176
+ "position": {
177
+ "x": 0,
178
+ "y": 0
179
+ },
180
+ "size": {
181
+ "w": 12,
182
+ "h": 40
183
+ }
184
+ }
185
+ ],
186
+ "events": [],
187
+ "validations": [],
188
+ "stateBindings": [],
189
+ "children": []
190
+ }
191
+ ]
192
+ },
193
+ {
194
+ "elementId": "shell_main",
195
+ "name": "shell_main",
196
+ "type": "container",
197
+ "kind": "component",
198
+ "props": {
199
+ "title": {
200
+ "type": "static",
201
+ "ref": null,
202
+ "data": ""
203
+ },
204
+ "layout": {
205
+ "type": "static",
206
+ "ref": null,
207
+ "data": "flex"
208
+ },
209
+ "direction": {
210
+ "type": "static",
211
+ "ref": null,
212
+ "data": "column"
213
+ },
214
+ "gap": {
215
+ "type": "static",
216
+ "ref": null,
217
+ "data": "none"
218
+ }
219
+ },
220
+ "states": {
221
+ "isDroppable": true
222
+ },
223
+ "appearance": {
224
+ "heightType": "fill",
225
+ "marginType": "none",
226
+ "paddingType": "none"
227
+ },
228
+ "blocks": [
229
+ {
230
+ "breakpoint": 1024,
231
+ "position": {
232
+ "x": 2,
233
+ "y": 0
234
+ },
235
+ "size": {
236
+ "w": 10,
237
+ "h": 80
238
+ }
239
+ }
240
+ ],
241
+ "events": [],
242
+ "validations": [],
243
+ "stateBindings": [],
244
+ "children": [
245
+ {
246
+ "elementId": "shell_topbar",
247
+ "name": "shell_topbar",
248
+ "type": "container",
249
+ "kind": "component",
250
+ "props": {
251
+ "title": {
252
+ "type": "static",
253
+ "ref": null,
254
+ "data": ""
255
+ },
256
+ "layout": {
257
+ "type": "static",
258
+ "ref": null,
259
+ "data": "flex"
260
+ },
261
+ "direction": {
262
+ "type": "static",
263
+ "ref": null,
264
+ "data": "row"
265
+ },
266
+ "verticalAlign": {
267
+ "type": "static",
268
+ "ref": null,
269
+ "data": "middle"
270
+ }
271
+ },
272
+ "states": {
273
+ "isDroppable": true,
274
+ "isSticky": true
275
+ },
276
+ "appearance": {
277
+ "heightType": "auto",
278
+ "marginType": "none",
279
+ "paddingType": "none"
280
+ },
281
+ "blocks": [
282
+ {
283
+ "breakpoint": 1024,
284
+ "position": {
285
+ "x": 0,
286
+ "y": 0
287
+ },
288
+ "size": {
289
+ "w": 12,
290
+ "h": 6
291
+ }
292
+ }
293
+ ],
294
+ "events": [],
295
+ "validations": [],
296
+ "stateBindings": [],
297
+ "children": [
298
+ {
299
+ "elementId": "shell_breadcrumb",
300
+ "name": "shell_breadcrumb",
301
+ "type": "breadcrumb",
302
+ "kind": "component",
303
+ "props": {},
304
+ "states": {},
305
+ "appearance": {
306
+ "heightType": "auto",
307
+ "marginType": "none",
308
+ "paddingType": "none"
309
+ },
310
+ "blocks": [
311
+ {
312
+ "breakpoint": 1024,
313
+ "position": {
314
+ "x": 0,
315
+ "y": 0
316
+ },
317
+ "size": {
318
+ "w": 12,
319
+ "h": 3
320
+ }
321
+ }
322
+ ],
323
+ "events": [],
324
+ "validations": [],
325
+ "stateBindings": [],
326
+ "children": []
327
+ }
328
+ ]
329
+ },
330
+ {
331
+ "elementId": "shell_slot",
332
+ "name": "shell_slot",
333
+ "type": "slot",
334
+ "kind": "component",
335
+ "props": {},
336
+ "states": {
337
+ "isScrollable": false
338
+ },
339
+ "appearance": {
340
+ "heightType": "fill",
341
+ "marginType": "none",
342
+ "paddingType": "none"
343
+ },
344
+ "blocks": [
345
+ {
346
+ "breakpoint": 1024,
347
+ "position": {
348
+ "x": 0,
349
+ "y": 6
350
+ },
351
+ "size": {
352
+ "w": 12,
353
+ "h": 74
354
+ }
355
+ }
356
+ ],
357
+ "events": [],
358
+ "validations": [],
359
+ "stateBindings": [],
360
+ "children": []
361
+ }
362
+ ]
363
+ }
364
+ ]
365
+ }
366
+ ]
367
+ }
368
+ }
@@ -5,6 +5,7 @@
5
5
  },
6
6
  "definition": {
7
7
  "name": "Get Products",
8
+ "description": "Ürün listesini sayfalı ve süzülebilir biçimde döner.",
8
9
  "slug": "get-products",
9
10
  "type": "rest-api",
10
11
  "httpMethod": "get",