@appaflytech/zeyrek-ai-mcp 0.0.6 → 0.0.8
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 +88 -0
- package/dist/knowledge/componentCatalog.js +415 -68
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/normalize.js +512 -25
- package/dist/knowledge/outlines.js +31 -0
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/propTypes.js +5 -4
- package/dist/knowledge/rules.js +453 -6
- package/dist/knowledge/styleResolve.js +6 -1
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +62 -3
- package/dist/platform/http.js +11 -2
- package/dist/platform/responses.js +16 -4
- package/dist/platform/screenshot.js +98 -9
- package/dist/platform/strictArguments.js +32 -0
- package/dist/platform/writeDiff.js +6 -3
- 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 +2 -2
- package/dist/resources/guides/component.md +14 -2
- package/dist/resources/guides/entity.md +321 -17
- package/dist/resources/guides/expressions.md +9 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +7 -0
- package/dist/resources/guides/layout.md +6 -0
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +166 -7
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +101 -22
- package/dist/resources/guides/translation.md +35 -10
- package/dist/resources/guides/workflow.md +51 -2
- 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 +123 -2
- package/dist/resources/schemas/CreateWorkflowCommand.json +17 -3
- 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 +123 -2
- package/dist/resources/schemas/PreviewWorkflowCommand.json +17 -3
- 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 +17 -3
- 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 +123 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +17 -3
- package/dist/server.js +17 -4
- 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 +104 -5
- package/dist/tools/functions.js +331 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/layouts.js +4 -4
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +112 -9
- package/dist/tools/pages.js +188 -66
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/queries.js +72 -6
- 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 +28 -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
|
+
}
|