@appaflytech/zeyrek-ai-mcp 0.0.6 → 0.0.7
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/.env.example +2 -2
- package/.env.production.example +8 -0
- package/README.md +9 -8
- package/dist/config.js +18 -3
- package/dist/knowledge/backendConstraints.js +85 -0
- package/dist/knowledge/componentCatalog.js +357 -54
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/normalize.js +53 -2
- package/dist/knowledge/outlines.js +31 -0
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/rules.js +343 -1
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +62 -3
- package/dist/platform/http.js +1 -0
- package/dist/platform/responses.js +16 -4
- package/dist/platform/screenshot.js +18 -1
- package/dist/resources/examples/flow-call-function.json +66 -0
- package/dist/resources/examples/function-total.json +70 -0
- package/dist/resources/examples/microfunction-node-sum.json +56 -0
- package/dist/resources/examples/page-dynamic-form.json +1 -1
- package/dist/resources/examples/page-sign-in.json +1 -1
- package/dist/resources/guides/component.md +13 -1
- package/dist/resources/guides/entity.md +321 -17
- package/dist/resources/guides/expressions.md +3 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +5 -0
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +61 -2
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +20 -4
- package/dist/resources/guides/translation.md +24 -0
- package/dist/resources/guides/workflow.md +1 -1
- package/dist/resources/schema-fingerprint.json +4 -4
- package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
- package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
- package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
- package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
- package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
- package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/CreateWorkflowCommand.json +13 -2
- package/dist/resources/schemas/DataJobDto.json +267 -0
- package/dist/resources/schemas/ExportRowsCommand.json +172 -0
- package/dist/resources/schemas/Filter.json +3 -1
- package/dist/resources/schemas/GetRowsQuery.json +3 -1
- package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
- package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
- package/dist/resources/schemas/ImportPlanDto.json +260 -0
- package/dist/resources/schemas/ImportSampleDto.json +90 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +13 -2
- package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
- package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
- package/dist/resources/schemas/RunImportCommand.json +62 -0
- package/dist/resources/schemas/StepDto.json +13 -2
- package/dist/resources/schemas/UpdateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +3 -1
- package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
- package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/UpdateWorkflowCommand.json +13 -2
- package/dist/server.js +15 -3
- package/dist/tools/activities.js +40 -0
- package/dist/tools/automations.js +14 -5
- package/dist/tools/common.js +16 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/environments.js +52 -0
- package/dist/tools/files.js +51 -50
- package/dist/tools/flows.js +95 -2
- package/dist/tools/functions.js +331 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +42 -3
- package/dist/tools/pages.js +62 -10
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/revisions.js +94 -0
- package/dist/tools/rows.js +342 -22
- package/dist/tools/screenWrite.js +11 -2
- package/dist/tools/templates.js +32 -5
- package/dist/tools/workflows.js +24 -6
- package/package.json +3 -1
|
@@ -6,10 +6,17 @@ export const componentEventContracts = {
|
|
|
6
6
|
pagination: { payload: { offset: 'number', limit: 'number' } },
|
|
7
7
|
filter: { payload: { filters: 'Array<{ field, operator, value, filterType, conditionType }>', offset: 'number' } },
|
|
8
8
|
sort: { payload: { sortField: 'string (kolonun name alanı)', sortType: "'asc' | 'desc' | '' (temizlendi)", offset: 'number' } },
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
search: {
|
|
10
|
+
payload: {
|
|
11
|
+
searchText: 'string (kutuya yazılan metin, KIRPILMAZ; kutu temizlenince "")',
|
|
12
|
+
offset: 'number (0’a döner)'
|
|
13
|
+
},
|
|
14
|
+
reads: 'isSearchable:true iken araç çubuğundaki kutudan: yazmaya ara verince (400 ms), Enter’da ve kutu boşaltılınca hemen yayılır; aynı metin ikinci kez yayılmaz (Enter dahil). Enter formu göndermez. Tablo satırları kendisi SÜZMEZ — akış self.searchText ile sorguyu yeniden çalıştırıp rows’u yazar'
|
|
15
|
+
},
|
|
16
|
+
select: { payload: {}, reads: 'self.selectedRow — satır tıklaması rowSelection:none dahil HER kipte yazar ve bu olayı çalıştırır; isReadonly:true iken hiç çalışmaz' },
|
|
17
|
+
selectionChange: { payload: {}, reads: 'self.selectedRows (dizi) + self.selectedRow — yalnız multi kipindeki onay kutularından (satır tıklaması tetiklemez)' },
|
|
11
18
|
doubleClick: { payload: {}, reads: 'self.selectedRow' },
|
|
12
|
-
rowChange: { payload: {}, reads: 'self.
|
|
19
|
+
rowChange: { payload: {}, reads: 'self.selectedRow (düzenlenen satır, YENİ değeriyle) — satır nesnesi self.rows içinde de yerinde güncellenir; değer gerçekten değişmediyse olay çalışmaz' }
|
|
13
20
|
},
|
|
14
21
|
datatable: {
|
|
15
22
|
mount: { payload: {}, reads: 'self.filters · self.sortField · self.sortType · self.offset · self.limit' }
|
|
@@ -32,8 +39,23 @@ export const componentEventContracts = {
|
|
|
32
39
|
},
|
|
33
40
|
phoneinput: {
|
|
34
41
|
mount: NO_DATA,
|
|
35
|
-
change: {
|
|
36
|
-
|
|
42
|
+
change: {
|
|
43
|
+
payload: {
|
|
44
|
+
value: 'string (E.164, ör. +905321234567)',
|
|
45
|
+
formattedValue: 'string (kullanıcının gördüğü satır: "+90 532 123 45 67"; boş alanda "")',
|
|
46
|
+
isValid: 'boolean (numara seçili ülke için TAM mı — boş alan false; zorunluluk kararı validations.required’ın işi)'
|
|
47
|
+
},
|
|
48
|
+
reads: "self’e yazılan tek anahtar `value`dur; `formattedValue` ve `isValid` düğüme YAZILMAZ, yalnız event.data’dadır (params). DEĞER DEĞİŞMEDİYSE YAYILMAZ — hane sınırına çarpan tuşlar ve süzülen harfler akış tetiklemez"
|
|
49
|
+
},
|
|
50
|
+
enter: {
|
|
51
|
+
payload: { value: 'string (E.164)', formattedValue: 'string', isValid: 'boolean' },
|
|
52
|
+
reads: 'Enter tuşu; change ile aynı yük. self.value’yu bu da yazar (platform emisyonun data’sını koşulsuz yazıyor, ARC-067) ama değer zaten change’in yazdığıdır — "gönder" kancası olarak kullan'
|
|
53
|
+
},
|
|
54
|
+
focus: NO_DATA,
|
|
55
|
+
blur: {
|
|
56
|
+
payload: { value: 'string (E.164)', formattedValue: 'string', isValid: 'boolean' },
|
|
57
|
+
reads: 'focus/blur SATIR düzeyindedir: numara alanı ile ülke seçicisi arasında gezinmek olay yaymaz, yalnız alanın tamamına girip çıkmak yayar. blur change’in aksine değer aynı olsa da yayılır ve yayılmış bir focus’un karşılığı alan sonradan donsa bile gelir; yükü self.value’yu yine yazar (ARC-067)'
|
|
58
|
+
}
|
|
37
59
|
},
|
|
38
60
|
filterbuilder: {
|
|
39
61
|
mount: NO_DATA,
|
|
@@ -62,24 +84,81 @@ export const componentEventContracts = {
|
|
|
62
84
|
},
|
|
63
85
|
'layout-region': { mount: NO_DATA },
|
|
64
86
|
navigations: { mount: NO_DATA },
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
// `change` her tuşta (input), `changed` değer kesinleşince (native change: blur/Enter) yayılır;
|
|
88
|
+
// ikisi de setter'dır — pahalı akışı (sorgu, kayıt) `changed`'e bağla.
|
|
89
|
+
textinput: {
|
|
90
|
+
mount: NO_DATA,
|
|
91
|
+
change: valueChange('string'),
|
|
92
|
+
changed: valueChange('string (blur/Enter ile kesinleşen değer)'),
|
|
93
|
+
enter: { payload: { value: 'string (Enter anındaki değer)' }, reads: "form içindeyse Enter formun submit akışını da çalıştırır — kaydı forma bağla, enter'a değil" },
|
|
94
|
+
focus: NO_DATA,
|
|
95
|
+
blur: NO_DATA
|
|
96
|
+
},
|
|
97
|
+
textarea: { mount: NO_DATA, change: valueChange('string'), changed: valueChange('string (blur ile kesinleşen değer)'), focus: NO_DATA, blur: NO_DATA },
|
|
98
|
+
password: {
|
|
99
|
+
mount: NO_DATA,
|
|
100
|
+
change: valueChange('string'),
|
|
101
|
+
changed: valueChange('string (blur/Enter ile kesinleşen değer)'),
|
|
102
|
+
enter: { payload: { value: 'string (Enter anındaki değer)' }, reads: "form içindeyse Enter formun submit akışını da çalıştırır — girişi forma bağla, enter'a değil" },
|
|
103
|
+
focus: NO_DATA,
|
|
104
|
+
blur: NO_DATA
|
|
105
|
+
},
|
|
106
|
+
// `change` her tuşta ve artı/eksi düğmesinde, `changed` değer kesinleşince (blur/Enter, düğme)
|
|
107
|
+
// yayılır; ikisi de setter'dır — pahalı akışı `changed`'e bağla. Boşaltılan alan 0 yayar (K-3).
|
|
108
|
+
numberinput: {
|
|
109
|
+
mount: NO_DATA,
|
|
110
|
+
change: valueChange('number (aralığa kırpılmış)'),
|
|
111
|
+
changed: valueChange('number (blur/Enter/artı-eksi ile kesinleşen değer)'),
|
|
112
|
+
enter: { payload: { value: 'number (Enter anında kesinleşen, kırpılmış değer)' }, reads: "form içindeyse Enter formun submit akışını da çalıştırır — kaydı forma bağla, enter'a değil" },
|
|
113
|
+
focus: NO_DATA,
|
|
114
|
+
blur: NO_DATA
|
|
115
|
+
},
|
|
71
116
|
incrementalinput: { mount: NO_DATA, change: valueChange('string'), blur: NO_DATA },
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
117
|
+
// Adlar 2026-09-11'de `on` önekinden kurtuldu: şema onMount/onChange diyordu, runtime
|
|
118
|
+
// mount/change yayıyordu ve flow-machine eşleşmeyi ADLA yaptığı için bu bileşene bağlanan
|
|
119
|
+
// hiçbir akış çalışmıyordu.
|
|
120
|
+
texteditor: {
|
|
121
|
+
mount: NO_DATA,
|
|
122
|
+
change: { payload: { value: "string (HTML; boş editörde boş metin)" }, reads: "setter: self.value da yazılır. HER TUŞTA yayılır — pahalı akışı changed'e bağla" },
|
|
123
|
+
changed: { payload: { value: "string (HTML; boş editörde boş metin)" }, reads: "setter: self.value da yazılır; yalnız odak çıkışında ve değer gerçekten değiştiyse yayılır" },
|
|
124
|
+
focus: NO_DATA,
|
|
125
|
+
blur: NO_DATA
|
|
126
|
+
},
|
|
127
|
+
checkbox: {
|
|
128
|
+
mount: NO_DATA,
|
|
129
|
+
change: { payload: { value: 'boolean' }, reads: "setter: self.value da yazılır. Her tıkta İKİ olay yayılır — change ve (yeni duruma göre) checked ya da unchecked; ikisini birden bağlarsan akış iki kez koşar" },
|
|
130
|
+
checked: { payload: { value: 'boolean (true)' }, reads: 'setter: self.value da yazılır; change ile AYNI tıkta yayılır' },
|
|
131
|
+
unchecked: { payload: { value: 'boolean (false)' }, reads: 'setter: self.value da yazılır; change ile AYNI tıkta yayılır' },
|
|
132
|
+
focus: NO_DATA,
|
|
133
|
+
blur: NO_DATA
|
|
134
|
+
},
|
|
135
|
+
switch: {
|
|
136
|
+
mount: NO_DATA,
|
|
137
|
+
change: { payload: { value: 'boolean' }, reads: "setter: self.value da yazılır. Her flip'te İKİ olay yayılır — change ve (yeni duruma göre) checked ya da unchecked; ikisini birden bağlarsan akış iki kez koşar" },
|
|
138
|
+
checked: { payload: { value: 'boolean (true)' }, reads: 'setter: self.value da yazılır; change ile AYNI flip’te yayılır' },
|
|
139
|
+
unchecked: { payload: { value: 'boolean (false)' }, reads: 'setter: self.value da yazılır; change ile AYNI flip’te yayılır' },
|
|
140
|
+
focus: NO_DATA,
|
|
141
|
+
blur: NO_DATA
|
|
142
|
+
},
|
|
75
143
|
radio: { change: valueChange('string') },
|
|
76
|
-
dropdown: { mount: NO_DATA, change:
|
|
77
|
-
'multi-select': { mount: NO_DATA, change:
|
|
78
|
-
'tag-input': { mount: NO_DATA, change:
|
|
144
|
+
dropdown: { mount: NO_DATA, change: { payload: { value: 'seçilen option değeri', selectedLabel: "seçilen option'ın label metni (temizlemede '')" } }, search: { payload: { searchText: 'string' } }, focus: NO_DATA, blur: NO_DATA },
|
|
145
|
+
'multi-select': { mount: NO_DATA, change: { payload: { value: "seçilen değerlerin dizisi: [{ value, label, id, text }] — id/text, value/label'ın eski adlı ikizleridir" } }, search: { payload: { searchText: 'string' } }, focus: NO_DATA, blur: NO_DATA },
|
|
146
|
+
'tag-input': { mount: NO_DATA, change: { payload: { value: 'etiket dizisi: [{ value, color? }] — renk verilmemiş etikette color anahtarı HİÇ bulunmaz' } }, search: { payload: { searchText: 'string' } }, focus: NO_DATA, blur: NO_DATA },
|
|
79
147
|
datepicker: { mount: NO_DATA, change: { payload: { value: 'string (tarih)', HH: 'string', mm: 'string' } } },
|
|
80
148
|
datetimepicker: { mount: NO_DATA, change: { payload: {}, reads: 'self.value' } },
|
|
81
149
|
daterangepicker: { mount: NO_DATA, change: { payload: { valueFrom: 'string', valueTo: 'string', HH: 'string', mm: 'string' } } },
|
|
82
|
-
datetimerangepicker: {
|
|
150
|
+
datetimerangepicker: {
|
|
151
|
+
mount: NO_DATA,
|
|
152
|
+
change: {
|
|
153
|
+
payload: {
|
|
154
|
+
valueFrom: "string 'yyyy-MM-ddTHH:mm:ss' | null (yerel duvar saati, dilim eki yok; yarı boşaltılınca null)",
|
|
155
|
+
valueTo: "string 'yyyy-MM-ddTHH:mm:ss' | null"
|
|
156
|
+
},
|
|
157
|
+
reads: "ikisi de düğümün prop'una yazılır: self.valueFrom / self.valueTo. Bir yarı yalnız tarih+saat ikisi de seçilince yayar. HH/mm anahtarları YOKTUR (2026-09-12'de kayıttan çıkarıldı — runtime hiç göndermiyordu)."
|
|
158
|
+
},
|
|
159
|
+
focus: NO_DATA,
|
|
160
|
+
blur: NO_DATA
|
|
161
|
+
},
|
|
83
162
|
fileUpload: { change: { payload: {}, reads: 'self.value' } },
|
|
84
163
|
fileInput: { change: { payload: { value: 'Array<{ type, value }>', fileName: 'string' } } },
|
|
85
164
|
filebutton: { change: { payload: { value: 'Array<{ type, value }>', fileName: 'string' } } },
|
|
@@ -118,9 +197,18 @@ export const componentEventContracts = {
|
|
|
118
197
|
// 2026-08-25c, `docs/20-komponent-bilgi-katmani.md` §9).
|
|
119
198
|
autocomplete: {
|
|
120
199
|
mount: NO_DATA,
|
|
121
|
-
change:
|
|
122
|
-
|
|
123
|
-
|
|
200
|
+
change: {
|
|
201
|
+
payload: { value: 'string (YAZILAN metin — option değeri değil)' },
|
|
202
|
+
reads: "setter: self.value da yazılır. Temizleme (×) ve strict geri alma aynı olayı { value: '', selectedValue: '', selectedLabel: '' } ile yayar — alan boşalınca seçim durumu da boşalır"
|
|
203
|
+
},
|
|
204
|
+
select: {
|
|
205
|
+
payload: {
|
|
206
|
+
selectedValue: "seçilen option'ın değeri",
|
|
207
|
+
selectedLabel: "seçilen option'ın etiketi"
|
|
208
|
+
},
|
|
209
|
+
reads: "self.value etiketi taşır; option'ın değeri için self.selectedValue (2026-09-11'e kadar yükün adları value/label idi ve alanın kendi prop'larını eziyordu)"
|
|
210
|
+
},
|
|
211
|
+
search: { payload: { searchText: 'string' }, reads: 'self.searchText' },
|
|
124
212
|
enter: { payload: { value: 'string (Enter anındaki metin; vurgulu öneri yokken)' } },
|
|
125
213
|
focus: NO_DATA,
|
|
126
214
|
blur: NO_DATA
|
|
@@ -191,13 +279,20 @@ export const componentEventContracts = {
|
|
|
191
279
|
blur: NO_DATA
|
|
192
280
|
},
|
|
193
281
|
statcard: { mount: NO_DATA, click: NO_DATA, footerClick: { payload: {}, reads: "footerFlow zaten doğrudan çalışır; footerHref varsa tıklama ona gider" } },
|
|
194
|
-
switcher: {
|
|
282
|
+
switcher: {
|
|
283
|
+
mount: NO_DATA,
|
|
284
|
+
change: { payload: { value: 'boolean' }, reads: "setter: self.value da yazılır. Her geçişte İKİ olay yayılır — change ve (yeni duruma göre) checked ya da unchecked; ikisini birden bağlarsan akış iki kez koşar" },
|
|
285
|
+
checked: { payload: { value: 'boolean (true)' }, reads: 'setter: self.value da yazılır; change ile AYNI geçişte yayılır' },
|
|
286
|
+
unchecked: { payload: { value: 'boolean (false)' }, reads: 'setter: self.value da yazılır; change ile AYNI geçişte yayılır' },
|
|
287
|
+
focus: NO_DATA,
|
|
288
|
+
blur: NO_DATA
|
|
289
|
+
},
|
|
195
290
|
timeline: {
|
|
196
291
|
mount: NO_DATA,
|
|
197
292
|
clickItem: { payload: { currentItem: 'tıklanan olay öğesi', index: 'number' }, reads: "öğenin kendi flow'u da doğrudan çalışır" },
|
|
198
293
|
showMore: NO_DATA
|
|
199
294
|
},
|
|
200
|
-
timepicker: { mount: NO_DATA, change:
|
|
295
|
+
timepicker: { mount: NO_DATA, change: { payload: { value: "string 'HH:mm' (temizlenince '')", selectedLabel: "seçilen saatin gösterilen metni — 12h kipinde '01:00 PM' (temizlenince '')" } }, focus: NO_DATA, blur: NO_DATA },
|
|
201
296
|
toast: {
|
|
202
297
|
open: NO_DATA,
|
|
203
298
|
close: NO_DATA,
|
|
@@ -270,9 +365,9 @@ export const componentEventContracts = {
|
|
|
270
365
|
mount: NO_DATA,
|
|
271
366
|
change: {
|
|
272
367
|
payload: { value: "string (PNG data-URL ya da HAM SVG işaretlemesi; temizlemede '')" },
|
|
273
|
-
reads:
|
|
368
|
+
reads: "setter: self.value da güncellenir, event.data.value da dolu gelir; her tamamlanan çizgide bir kez yayılır. Ayrıca outputFormat/penColor/penWidth değişince mevcut vuruşlar yeniden dışa aktarılır ve change bir kez daha yayılır. Akış value'yu setter ile yazdığında ped sıfırlanır ama change YAYILMAZ — kendi yazdığın değer geri gelmez"
|
|
274
369
|
},
|
|
275
|
-
clear: { payload: {}, reads: "
|
|
370
|
+
clear: { payload: {}, reads: "yalnız kullanıcı Clear düğmesine bastığında yayılır; önce change('') gelir, sonra clear — değeri change'ten oku. Akıştan value'yu '' yapmak pedi temizler ama hiçbir olay yaymaz" }
|
|
276
371
|
},
|
|
277
372
|
stepper: {
|
|
278
373
|
mount: NO_DATA,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export const FUNCTIONS_NAMESPACE = 'Functions';
|
|
2
|
+
const NAME_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
3
|
+
/** JavaScript ayrılmış sözcükleri — `GlobalFunctionRules.ReservedWordNames` ile aynı liste. */
|
|
4
|
+
export const RESERVED_WORDS = new Set([
|
|
5
|
+
'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
|
|
6
|
+
'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if',
|
|
7
|
+
'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private',
|
|
8
|
+
'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try',
|
|
9
|
+
'typeof', 'var', 'void', 'while', 'with', 'yield'
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Workflow motorunun global adları — `GlobalFunctionRules.WorkflowReservedNames` ile birebir.
|
|
13
|
+
* `source:'workflow'` fonksiyonu bu adı alırsa motorun kendi nesnesini ezerdi (platform 422).
|
|
14
|
+
*/
|
|
15
|
+
export const WORKFLOW_RESERVED_NAMES = new Set([
|
|
16
|
+
'Authorized', 'body', 'convert', 'ConvertToCsv', 'ConvertToPdf', 'cookie', 'crypto', 'DecodeQr', 'DeepEqual', 'error', 'file',
|
|
17
|
+
'GenerateAccessToken', 'GenerateGuid', 'GenerateJwt', 'GeneratePKCE', 'Hash', 'header', 'input', 'log', 'Log', 'Now',
|
|
18
|
+
'queryString', 'request', 'RequestPathAndQuery', 'RequestUrl', 'route', 'system', 'Today', 'ToTimeZone', 'UtcNow', 'ValidateAccessToken'
|
|
19
|
+
]);
|
|
20
|
+
export function sourceOf(definition) {
|
|
21
|
+
return definition.source === 'workflow' ? 'workflow' : 'studio';
|
|
22
|
+
}
|
|
23
|
+
export function isValidParameterName(name) {
|
|
24
|
+
return typeof name === 'string' && NAME_PATTERN.test(name) && !RESERVED_WORDS.has(name);
|
|
25
|
+
}
|
|
26
|
+
export function isValidFunctionName(name, source = 'studio') {
|
|
27
|
+
return isValidParameterName(name) && (source === 'workflow' ? !WORKFLOW_RESERVED_NAMES.has(name) : name !== FUNCTIONS_NAMESPACE);
|
|
28
|
+
}
|
|
29
|
+
/** Geçersiz adın NEDENİ — mesaj kullanıcıya hangi kuralı bozduğunu söylesin diye. */
|
|
30
|
+
export function nameProblem(name, source = 'studio') {
|
|
31
|
+
if (typeof name !== 'string' || name.trim() === '')
|
|
32
|
+
return 'boş';
|
|
33
|
+
if (!NAME_PATTERN.test(name))
|
|
34
|
+
return `JS tanımlayıcısı değil (${NAME_PATTERN.source})`;
|
|
35
|
+
if (RESERVED_WORDS.has(name))
|
|
36
|
+
return 'JavaScript ayrılmış sözcüğü';
|
|
37
|
+
if (source === 'workflow' && WORKFLOW_RESERVED_NAMES.has(name))
|
|
38
|
+
return `workflow motorunun global adı ('${name}' — system, input, Now… ezilemez)`;
|
|
39
|
+
if (source === 'studio' && name === FUNCTIONS_NAMESPACE)
|
|
40
|
+
return `'${FUNCTIONS_NAMESPACE}' ad alanının kendisi (ayrılmış ad, K1)`;
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
/** Çağrı biçimi: studio `Functions.<ad>`, workflow çıplak `<ad>`. */
|
|
44
|
+
export function callPrefix(source) {
|
|
45
|
+
return source === 'workflow' ? '' : `${FUNCTIONS_NAMESPACE}.`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Studio'nun canlı imza önizlemesiyle aynı biçim: `Functions.toplamHesapla(fiyat, adet = 1)`;
|
|
49
|
+
* workflow kaynağında öneksiz `toplamHesapla(fiyat, adet = 1)`. Varsayılanı olmayan ve zorunlu
|
|
50
|
+
* olmayan parametre `ad?` yazılır.
|
|
51
|
+
*/
|
|
52
|
+
export function functionSignature(definition) {
|
|
53
|
+
const parameters = Array.isArray(definition.parameters) ? definition.parameters : [];
|
|
54
|
+
const rendered = parameters
|
|
55
|
+
.filter(parameter => parameter && typeof parameter === 'object')
|
|
56
|
+
.map(parameter => {
|
|
57
|
+
const name = String(parameter.name ?? '?');
|
|
58
|
+
if (parameter.defaultValue !== undefined && parameter.defaultValue !== null)
|
|
59
|
+
return `${name} = ${JSON.stringify(parameter.defaultValue)}`;
|
|
60
|
+
return parameter.required === true ? name : `${name}?`;
|
|
61
|
+
});
|
|
62
|
+
return `${callPrefix(sourceOf(definition))}${String(definition.name ?? '?')}(${rendered.join(', ')})`;
|
|
63
|
+
}
|
|
64
|
+
const CALL_PATTERN = /\bFunctions\s*\.\s*([A-Za-z_$][\w$]*)\s*\(/g;
|
|
65
|
+
/**
|
|
66
|
+
* Bir ifadedeki `Functions.<ad>(` çağrılarını ve KABA argüman sayısını döner. Sayım parantez
|
|
67
|
+
* dengesiyle üst düzey virgülleri sayar; string/şablon ve iç içe çağrı içindeki virgül
|
|
68
|
+
* sayılmaz. Yine de yaklaşıktır — çağıran yalnız FAZLA argümanda uyarır, eksik argümanı
|
|
69
|
+
* `required` kuralına bırakır (K5: eşleme konumsaldır, `undefined` → varsayılan).
|
|
70
|
+
*/
|
|
71
|
+
export function functionCallRefs(expression) {
|
|
72
|
+
const refs = [];
|
|
73
|
+
let match;
|
|
74
|
+
CALL_PATTERN.lastIndex = 0;
|
|
75
|
+
while ((match = CALL_PATTERN.exec(expression)) !== null) {
|
|
76
|
+
refs.push({ name: match[1] ?? '', argCount: countArguments(expression, match.index + match[0].length), index: match.index });
|
|
77
|
+
}
|
|
78
|
+
return refs.filter(ref => ref.name !== '');
|
|
79
|
+
}
|
|
80
|
+
function countArguments(source, start) {
|
|
81
|
+
let depth = 0;
|
|
82
|
+
let commas = 0;
|
|
83
|
+
let quote;
|
|
84
|
+
let sawToken = false;
|
|
85
|
+
for (let index = start; index < source.length; index++) {
|
|
86
|
+
const char = source[index];
|
|
87
|
+
if (quote !== undefined) {
|
|
88
|
+
if (char === '\\') {
|
|
89
|
+
index++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (char === quote)
|
|
93
|
+
quote = undefined;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (char === '\'' || char === '"' || char === '`') {
|
|
97
|
+
quote = char;
|
|
98
|
+
sawToken = true;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === '(' || char === '[' || char === '{') {
|
|
102
|
+
depth++;
|
|
103
|
+
sawToken = true;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (char === ')' || char === ']' || char === '}') {
|
|
107
|
+
if (depth === 0)
|
|
108
|
+
return sawToken ? commas + 1 : 0;
|
|
109
|
+
depth--;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (char === ',' && depth === 0) {
|
|
113
|
+
commas++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!/\s/.test(char))
|
|
117
|
+
sawToken = true;
|
|
118
|
+
}
|
|
119
|
+
return sawToken ? commas + 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
function escapeRegex(value) {
|
|
122
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
123
|
+
}
|
|
124
|
+
/** Çıplak `<ad>(` çağrısı — backend `GlobalFunction.IsCalledIn`'in workflow kalıbıyla aynı sınırlar. */
|
|
125
|
+
export function callsBareName(name, expression) {
|
|
126
|
+
return name !== '' && new RegExp(`(?<![A-Za-z0-9_$.])${escapeRegex(name)}\\s*\\(`).test(expression);
|
|
127
|
+
}
|
|
128
|
+
/** Gövde kendi adını çağırıyor mu — studio `Functions.<ad>(`, workflow çıplak `<ad>(` (özyineleme, FN-003). */
|
|
129
|
+
export function callsItself(name, body, source = 'studio') {
|
|
130
|
+
return source === 'workflow' ? callsBareName(name, body) : functionCallRefs(body).some(ref => ref.name === name);
|
|
131
|
+
}
|
|
132
|
+
export function mentionsAwait(body) {
|
|
133
|
+
return /\bawait\b/.test(body);
|
|
134
|
+
}
|
|
@@ -80,6 +80,33 @@ function fillRootAlias(table, fixes) {
|
|
|
80
80
|
table.alias = alias;
|
|
81
81
|
fixes.push(`kök alias '${alias}' olarak dolduruldu — join'li table'da kök alias boşsa SQL'e '.<alan>' gider ve Postgres 42601 (zero-length delimited identifier) döner`);
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* S-06 (2026-09-09 koşusu): yazma alanlarında `alias` sözleşmede YOKTUR. Okuma/dönüş seçimleri
|
|
85
|
+
* `TableSelect` ile modellenir ve `Alias`/`Path`/`FieldSlug` taşır; yazılacak alanlar ise
|
|
86
|
+
* `TableField` ile — o sınıfta yalnız `FieldId`, `Source`, `Value`, `FolderId`, `Order`,
|
|
87
|
+
* `WriteOn` vardır (`MicroserviceTableGetParameter.cs:326`; aynı tip `WorkflowDto.cs:252`
|
|
88
|
+
* table step kuralında da kullanılıyor). Gönderilen `alias` model bağlamada düşer, yanıt onu
|
|
89
|
+
* geri getirmez ve `silentlyDropped` her yazma çağrısında sahte bir kayıp satırı basardı.
|
|
90
|
+
* Alan gövdeden **istek gitmeden** çıkarılır ve `gatewayNormalized` ile bildirilir — kayıp değil,
|
|
91
|
+
* sözleşme dışı bir alan.
|
|
92
|
+
*
|
|
93
|
+
* Sıra önemlidir: `fillRootAlias` kök alias'ı türetirken `fields[].alias`'ı da aday sayar, bu
|
|
94
|
+
* yüzden ayıklama ondan SONRA çalışır.
|
|
95
|
+
*/
|
|
96
|
+
function dropWriteFieldAliases(table, fixes) {
|
|
97
|
+
const dropped = [];
|
|
98
|
+
for (const field of asObjects(table.fields)) {
|
|
99
|
+
const alias = String(field.alias ?? '').trim();
|
|
100
|
+
if (!Object.hasOwn(field, 'alias'))
|
|
101
|
+
continue;
|
|
102
|
+
delete field.alias;
|
|
103
|
+
if (alias)
|
|
104
|
+
dropped.push(`${alias}.${String(field.fieldSlug ?? field.fieldId ?? '?')}`);
|
|
105
|
+
}
|
|
106
|
+
if (dropped.length === 0)
|
|
107
|
+
return;
|
|
108
|
+
fixes.push(`fields[].alias gövdeden çıkarıldı (${dropped.join(', ')}): yazma alanları TableField ile modelleniyor ve o sözleşmede alias YOK — alias yalnız selectFields / returnFields / filters / joins için geçerlidir (platform_guide('microservice') §Alias nerede geçerli)`);
|
|
109
|
+
}
|
|
83
110
|
/**
|
|
84
111
|
* Platform sort/limit'i null bırakır ve path'i türetmez; Studio ikisini de guard'sız okur.
|
|
85
112
|
* Eksik olanları doldurur, dolu olanlara DOKUNMAZ (yanlış değerleri MS-007 block'u yakalar).
|
|
@@ -104,6 +131,7 @@ export function normalizeTableParameters(parameters, fallbackEntityId) {
|
|
|
104
131
|
fillJoinPaths(rootEntityId, table.joins, [], aliasChains, fixes);
|
|
105
132
|
fillSelectionPaths(table, aliasChains, fixes);
|
|
106
133
|
}
|
|
134
|
+
dropWriteFieldAliases(table, fixes);
|
|
107
135
|
return fixes;
|
|
108
136
|
}
|
|
109
137
|
export function normalizeMicroserviceDefinition(definition) {
|
|
@@ -259,6 +287,15 @@ export function normalizeFlowSteps(steps) {
|
|
|
259
287
|
const list = asObjects(steps);
|
|
260
288
|
const fixes = normalizeFlowHostGaps(steps, list);
|
|
261
289
|
for (const step of list) {
|
|
290
|
+
if (step.type === 'function') {
|
|
291
|
+
const args = (step.args ?? {});
|
|
292
|
+
if (!Array.isArray(args.parameters)) {
|
|
293
|
+
args.parameters = [];
|
|
294
|
+
step.args = args;
|
|
295
|
+
fixes.push(`'${String(step.stepId ?? step.name ?? '?')}': args.parameters=[] yazıldı — function adımı parametreleri dizi bekler; eşleme bildirimdeki ada göre, sıra konumsal (K5)`);
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
262
299
|
if (step.type !== 'microservice')
|
|
263
300
|
continue;
|
|
264
301
|
const args = (step.args ?? {});
|
|
@@ -822,6 +859,17 @@ export function sizeStringNote(fixed) {
|
|
|
822
859
|
return [];
|
|
823
860
|
return [`Boyut string'i normalize edildi (${fixed.join(', ')}): Studio paneli çıplak sayıyı px'e çevirerek yazar (appearance-size.functions.ts buildFlexSizeValue), gateway aynı biçimi üretir`];
|
|
824
861
|
}
|
|
862
|
+
/**
|
|
863
|
+
* U-05 (2026-09-09 koşusu): platform yazılmamış bir `appearance` alanını gövdede `null` olarak
|
|
864
|
+
* döndürüyor — `page_container` her yeni sayfada `widthType: null` ile geliyor. Yanıt katmanı
|
|
865
|
+
* null'ları eliyor (`responses.ts` `prune`), yani ajan öyle bir alan GÖRMÜYOR; lint ise ham
|
|
866
|
+
* gövdeye baktığı için `String(null)` = `'null'` diye bilinmeyen mod uyarısı basıyordu. Her yeni
|
|
867
|
+
* sayfanın ilk `page_schema_patch`'inde çıkan, kullanıcının düzeltemeyeceği gürültü buydu.
|
|
868
|
+
* `null` ve boş string = **yazılmamış**; `widthString`/`heightString` zaten böyle davranıyor.
|
|
869
|
+
*/
|
|
870
|
+
function sizeMode(raw) {
|
|
871
|
+
return raw === undefined || raw === null || raw === '' ? undefined : String(raw);
|
|
872
|
+
}
|
|
825
873
|
/**
|
|
826
874
|
* İki uyarı tek yürüyüşte:
|
|
827
875
|
*
|
|
@@ -832,6 +880,9 @@ export function sizeStringNote(fixed) {
|
|
|
832
880
|
* - **geçersiz değer / bilinmeyen mod** (`invalidCode`): `normalizeCssSize`'dan geçmeyen
|
|
833
881
|
* string ya da `WIDTH_TYPES` dışı `widthType` — platform 200 döner ama değer sessizce
|
|
834
882
|
* düşer (eski backend bilinmeyen enum'u da düşürür).
|
|
883
|
+
*
|
|
884
|
+
* Uyarı yalnız **yazılmış** değer için çıkar: `null` ve boş string yazılmamış sayılır
|
|
885
|
+
* (`sizeMode`). Boyut string'leri bunu zaten yapıyordu, modlar yapmıyordu.
|
|
835
886
|
*/
|
|
836
887
|
export function flexSizeWarnings(nodes, mismatchCode, invalidCode) {
|
|
837
888
|
const warnings = [];
|
|
@@ -839,8 +890,8 @@ export function flexSizeWarnings(nodes, mismatchCode, invalidCode) {
|
|
|
839
890
|
for (const node of list) {
|
|
840
891
|
const appearance = appearanceOf(node);
|
|
841
892
|
const label = String(node.elementId ?? node.name ?? node.type ?? '?');
|
|
842
|
-
const widthType =
|
|
843
|
-
const heightType =
|
|
893
|
+
const widthType = sizeMode(appearance.widthType);
|
|
894
|
+
const heightType = sizeMode(appearance.heightType);
|
|
844
895
|
if (widthType !== undefined && !WIDTH_TYPES.has(widthType)) {
|
|
845
896
|
warnings.push(`${invalidCode}: '${label}' düğümünde bilinmeyen widthType '${widthType}' — geçerli küme: cover · contain · auto · fixed · fill. Platform kabul eder ama renderer okumaz, eski backend enum'u sessizce düşürür.`);
|
|
846
897
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { functionSignature } from './functionGuards.js';
|
|
1
2
|
function asArray(value) {
|
|
2
3
|
return Array.isArray(value) ? value.filter((entry) => Boolean(entry) && typeof entry === 'object') : [];
|
|
3
4
|
}
|
|
@@ -296,3 +297,33 @@ export function resolvePatchOperations(document, operations, readTool = 'workflo
|
|
|
296
297
|
});
|
|
297
298
|
return { operations: resolved, notes, targets };
|
|
298
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Fonksiyon özeti (G-07): kaynak, ad, kapsam, imza, parametre listesi, gövde satır sayısı —
|
|
302
|
+
* gövdenin kendisi TAŞINMAZ (liste yanıtı MAX_RESPONSE_BYTES'ı şişirmesin). `function_patch`
|
|
303
|
+
* AÇILMADI: fonksiyonda tek büyük alan gövdedir ve gövde bütün olarak yazılır; kısmi güncellemeyi
|
|
304
|
+
* `function_update` mevcut kaydı okuyup eksik alanları doldurarak sağlar.
|
|
305
|
+
*/
|
|
306
|
+
export function functionOutline(definition) {
|
|
307
|
+
const parameters = asArray(definition.parameters);
|
|
308
|
+
const source = definition.source === 'workflow' ? 'workflow' : 'studio';
|
|
309
|
+
return {
|
|
310
|
+
id: definition.id,
|
|
311
|
+
source,
|
|
312
|
+
name: definition.name,
|
|
313
|
+
applicationId: definition.applicationId ?? null,
|
|
314
|
+
scope: source === 'workflow' ? null : (definition.scope ?? 'application'),
|
|
315
|
+
pageId: definition.pageId ?? null,
|
|
316
|
+
isAsync: definition.isAsync === true,
|
|
317
|
+
signature: functionSignature(definition),
|
|
318
|
+
parameters: parameters.map(parameter => ({
|
|
319
|
+
name: parameter.name,
|
|
320
|
+
type: parameter.type ?? 'any',
|
|
321
|
+
required: parameter.required === true,
|
|
322
|
+
defaultValue: parameter.defaultValue ?? null,
|
|
323
|
+
description: parameter.description ?? null
|
|
324
|
+
})),
|
|
325
|
+
description: definition.description ?? null,
|
|
326
|
+
bodyLines: typeof definition.body === 'string' && definition.body.length > 0 ? definition.body.split('\n').length : 0,
|
|
327
|
+
view: 'outline'
|
|
328
|
+
};
|
|
329
|
+
}
|
package/dist/knowledge/policy.js
CHANGED
|
@@ -11,6 +11,19 @@ export class PolicyBlockError extends Error {
|
|
|
11
11
|
this.name = 'PolicyBlockError';
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* `POLICY=warn` ve `policyOverride` kaçışları kuralın nereden geldiğine bakmaz: senkron
|
|
16
|
+
* `check`'lerden gelen bulgular da, platformdan okunarak üretilen bulgular da (WF-MF-001'in
|
|
17
|
+
* yayın durumu ön-denetimi) aynı kapıdan geçer. İki yerde iki farklı indirgeme olursa bir
|
|
18
|
+
* bulgu `policyOverride: true` ile de isteği durdurur ve kaçış yalanı çıkar.
|
|
19
|
+
*/
|
|
20
|
+
export function applyPolicyMode(config, findings, policyOverride = false) {
|
|
21
|
+
if (config.policyMode !== 'warn' && !policyOverride)
|
|
22
|
+
return findings;
|
|
23
|
+
return findings.map(finding => finding.severity === 'block'
|
|
24
|
+
? { ...finding, severity: 'warn', message: `${finding.message} [block → warn: ${policyOverride ? 'policyOverride' : 'POLICY=warn'}]` }
|
|
25
|
+
: finding);
|
|
26
|
+
}
|
|
14
27
|
export function evaluatePolicy(config, rules, module, definition, context, policyOverride = false) {
|
|
15
28
|
if (config.policyMode === 'off') {
|
|
16
29
|
logStep('politika', `${module}/${context.operation} · POLICY=off — lint çalıştırılmadı`);
|
|
@@ -18,11 +31,7 @@ export function evaluatePolicy(config, rules, module, definition, context, polic
|
|
|
18
31
|
}
|
|
19
32
|
const applicable = rules.filter(rule => rule.module === module && rule.appliesTo.includes(context.operation));
|
|
20
33
|
const raw = applicable.flatMap(rule => rule.check(definition, context));
|
|
21
|
-
const findings = config
|
|
22
|
-
? raw.map(finding => finding.severity === 'block'
|
|
23
|
-
? { ...finding, severity: 'warn', message: `${finding.message} [block → warn: ${policyOverride ? 'policyOverride' : 'POLICY=warn'}]` }
|
|
24
|
-
: finding)
|
|
25
|
-
: raw;
|
|
34
|
+
const findings = applyPolicyMode(config, raw, policyOverride);
|
|
26
35
|
logStep('politika', `${module}/${context.operation}${context.workflowKind ? ` (${context.workflowKind})` : ''} · ${applicable.length} kural denendi · ${findings.length === 0 ? 'bulgu yok' : `${findings.length} bulgu`}${policyOverride ? ' · policyOverride açık' : ''}`, findings.map(finding => `[${finding.ruleId}] ${finding.severity}: ${finding.message}`));
|
|
27
36
|
return findings;
|
|
28
37
|
}
|