@appaflytech/zeyrek-ai-mcp 0.0.35 → 0.0.37

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 (210) hide show
  1. package/.env.example +15 -0
  2. package/README.md +5 -5
  3. package/dist/config.js +37 -0
  4. package/dist/index.js +2 -0
  5. package/dist/knowledge/access.js +802 -0
  6. package/dist/knowledge/backendConstraints.js +18 -1
  7. package/dist/knowledge/case.js +41 -0
  8. package/dist/knowledge/componentCatalog.js +655 -424
  9. package/dist/knowledge/componentEvents.js +140 -23
  10. package/dist/knowledge/connectDataBind.js +843 -0
  11. package/dist/knowledge/connectDataDefinition.js +344 -0
  12. package/dist/knowledge/connectDataExecute.js +93 -0
  13. package/dist/knowledge/connectDataRules.js +128 -0
  14. package/dist/knowledge/decision.js +121 -0
  15. package/dist/knowledge/expressionPrelude.js +59 -0
  16. package/dist/knowledge/layoutSummary.js +140 -2
  17. package/dist/knowledge/microserviceParameters.js +58 -0
  18. package/dist/knowledge/mining.js +94 -0
  19. package/dist/knowledge/mobileComponentCatalog.js +864 -0
  20. package/dist/knowledge/mobileComponentEvents.js +239 -0
  21. package/dist/knowledge/mobileStore.js +321 -0
  22. package/dist/knowledge/normalize.js +235 -84
  23. package/dist/knowledge/outlines.js +36 -0
  24. package/dist/knowledge/platform/lint.js +385 -0
  25. package/dist/knowledge/platform/mobile.js +142 -0
  26. package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
  27. package/dist/knowledge/platform/mobileRules.js +231 -0
  28. package/dist/knowledge/platform/mobileSpacing.js +82 -0
  29. package/dist/knowledge/platform/mobileTabs.js +108 -0
  30. package/dist/knowledge/platform/nativeBoxModel.js +44 -0
  31. package/dist/knowledge/platform/profile.js +25 -0
  32. package/dist/knowledge/platform/resolve.js +122 -0
  33. package/dist/knowledge/platform/web.js +39 -0
  34. package/dist/knowledge/policy.js +18 -4
  35. package/dist/knowledge/process.js +510 -0
  36. package/dist/knowledge/reservedTables.js +79 -0
  37. package/dist/knowledge/rowFilter.js +43 -0
  38. package/dist/knowledge/rules.js +423 -37
  39. package/dist/knowledge/schemaGuard.js +11 -0
  40. package/dist/knowledge/styleResolve.js +53 -19
  41. package/dist/knowledge/styleResolveNative.js +527 -0
  42. package/dist/knowledge/themeResolve.js +4 -4
  43. package/dist/knowledge/themeSchema.js +1 -2
  44. package/dist/observability/trace.js +6 -3
  45. package/dist/platform/argumentAliases.js +128 -0
  46. package/dist/platform/bodyFile.js +103 -0
  47. package/dist/platform/capture/actions.js +398 -0
  48. package/dist/platform/capture/browser.js +1 -0
  49. package/dist/platform/capture/capture.js +683 -0
  50. package/dist/platform/capture/console-log.js +122 -0
  51. package/dist/platform/capture/index.js +11 -0
  52. package/dist/platform/capture/readiness.js +103 -0
  53. package/dist/platform/capture/summary.js +77 -0
  54. package/dist/platform/capture/target.js +5 -0
  55. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  56. package/dist/platform/capture/targets/studio-web.js +5 -0
  57. package/dist/platform/diagnostics.js +10 -4
  58. package/dist/platform/domSummary.js +1 -1
  59. package/dist/platform/errors.js +163 -10
  60. package/dist/platform/http.js +29 -8
  61. package/dist/platform/impersonation.js +45 -0
  62. package/dist/platform/jsonArray.js +24 -0
  63. package/dist/platform/responses.js +39 -5
  64. package/dist/platform/schemaReadMemory.js +60 -0
  65. package/dist/platform/secrets.js +19 -1
  66. package/dist/platform/strictArguments.js +68 -12
  67. package/dist/platform/tokenVault.js +187 -4
  68. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  69. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  70. package/dist/resources/examples/bpm-satinalma.json +376 -0
  71. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  72. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  73. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  74. package/dist/resources/examples/connect-data-query-params.json +50 -0
  75. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  76. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  77. package/dist/resources/examples/middleware-authenticate.json +1 -1
  78. package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
  79. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  80. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  81. package/dist/resources/examples/mobile-form-screen.json +365 -0
  82. package/dist/resources/examples/mobile-list-detail-form.json +703 -0
  83. package/dist/resources/examples/page-detail-profile.json +2 -1
  84. package/dist/resources/examples/page-dynamic-form.json +3 -2
  85. package/dist/resources/examples/page-flex-sections.json +1 -1
  86. package/dist/resources/examples/page-html-embed.json +1 -1
  87. package/dist/resources/examples/workflow-basic.json +8 -2
  88. package/dist/resources/guides/access.md +810 -0
  89. package/dist/resources/guides/bpm.md +691 -0
  90. package/dist/resources/guides/case.md +153 -0
  91. package/dist/resources/guides/component.md +93 -6
  92. package/dist/resources/guides/decision.md +185 -0
  93. package/dist/resources/guides/entity.md +50 -8
  94. package/dist/resources/guides/expressions.md +14 -5
  95. package/dist/resources/guides/functions.md +1 -1
  96. package/dist/resources/guides/getting-started.md +38 -1
  97. package/dist/resources/guides/layout.md +11 -7
  98. package/dist/resources/guides/merge.md +37 -3
  99. package/dist/resources/guides/microservice.md +87 -2
  100. package/dist/resources/guides/middleware.md +68 -4
  101. package/dist/resources/guides/mobile-layout.md +193 -0
  102. package/dist/resources/guides/mobile-publish.md +124 -0
  103. package/dist/resources/guides/mobile.md +549 -0
  104. package/dist/resources/guides/page.md +463 -83
  105. package/dist/resources/guides/theme.md +8 -3
  106. package/dist/resources/guides/translation.md +1 -1
  107. package/dist/resources/guides/workflow.md +58 -1
  108. package/dist/resources/read.js +41 -0
  109. package/dist/resources/rules.js +11 -5
  110. package/dist/resources/schema-fingerprint.json +7 -5
  111. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  112. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  113. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  114. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  115. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  116. package/dist/resources/schemas/AppSessionDto.json +53 -0
  117. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  118. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  119. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  120. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  121. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  122. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  123. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  124. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  125. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  126. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  127. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  128. package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
  129. package/dist/resources/schemas/DataJobDto.json +6 -1
  130. package/dist/resources/schemas/DecisionDto.json +302 -0
  131. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  132. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  133. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  134. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +25 -1
  135. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  136. package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
  137. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  138. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  139. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  140. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  141. package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
  142. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  143. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  144. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  145. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  146. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  147. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  148. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  149. package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
  150. package/dist/resources/schemas/StepDto.json +25 -1
  151. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  152. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  153. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  154. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  155. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  156. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  157. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  158. package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
  159. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +274 -0
  160. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  161. package/dist/server.js +31 -1
  162. package/dist/tools/access.js +369 -2
  163. package/dist/tools/accessOperations.js +135 -0
  164. package/dist/tools/accessPolicies.js +170 -0
  165. package/dist/tools/accessRuntime.js +203 -0
  166. package/dist/tools/applications.js +60 -3
  167. package/dist/tools/bpm.js +343 -0
  168. package/dist/tools/bpmGovernance.js +266 -0
  169. package/dist/tools/bpmOperations.js +230 -0
  170. package/dist/tools/bpmReporting.js +154 -0
  171. package/dist/tools/bpmRouting.js +150 -0
  172. package/dist/tools/bpmRuntime.js +396 -0
  173. package/dist/tools/case.js +178 -0
  174. package/dist/tools/caseRuntime.js +89 -0
  175. package/dist/tools/common.js +10 -1
  176. package/dist/tools/components.js +118 -14
  177. package/dist/tools/connectData.js +450 -0
  178. package/dist/tools/connectors.js +193 -0
  179. package/dist/tools/datasources.js +3 -3
  180. package/dist/tools/decisions.js +206 -0
  181. package/dist/tools/dependencies.js +1 -1
  182. package/dist/tools/deployments.js +38 -3
  183. package/dist/tools/designSchemaPatch.js +34 -9
  184. package/dist/tools/dtos.js +36 -5
  185. package/dist/tools/entities.js +79 -12
  186. package/dist/tools/externalComponents.js +26 -5
  187. package/dist/tools/flows.js +119 -17
  188. package/dist/tools/folders.js +1 -1
  189. package/dist/tools/imports.js +5 -2
  190. package/dist/tools/layouts.js +50 -17
  191. package/dist/tools/merges.js +2 -2
  192. package/dist/tools/microservices.js +77 -25
  193. package/dist/tools/mobile.js +280 -0
  194. package/dist/tools/mobileStore.js +556 -0
  195. package/dist/tools/modules.js +48 -0
  196. package/dist/tools/organizationStructure.js +137 -0
  197. package/dist/tools/pages.js +465 -98
  198. package/dist/tools/platformInfo.js +28 -8
  199. package/dist/tools/profileWrite.js +92 -0
  200. package/dist/tools/projects.js +71 -9
  201. package/dist/tools/rows.js +69 -13
  202. package/dist/tools/schemaOps.js +61 -1
  203. package/dist/tools/screenWrite.js +36 -7
  204. package/dist/tools/screens.js +95 -5
  205. package/dist/tools/templates.js +26 -1
  206. package/dist/tools/translations.js +1 -1
  207. package/dist/tools/variables.js +11 -5
  208. package/dist/tools/workflows.js +10 -5
  209. package/package.json +2 -2
  210. package/dist/platform/screenshot.js +0 -1162
@@ -0,0 +1,344 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Connect Data tanımı (`{ type: 'connectData', ref, data }` değerinin `ref`'i) — backend
4
+ * `ConnectDataDefinitionModel`'in ve screen-builder `connect-data.types.ts`'in aynası. Anahtar kümesi
5
+ * `test/connect-data-parity.test.ts` ile üç yerde eşit tutulur. Şema `strict`tir: platform bilinmeyen
6
+ * anahtarı `ConnectDataDefinitionInvalidException` ile reddeder, gateway aynı kararı istek gitmeden verir.
7
+ */
8
+ export const CONNECT_DATA_SOURCE_KINDS = ['entity', 'query', 'view', 'workflow', 'microservice', 'datasource'];
9
+ export const CONNECT_DATA_OPERATORS = ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'contains', 'icontains', 'in', 'notin', 'startswith', 'endswith', 'istartswith', 'iendswith', 'isnull', 'isnotnull'];
10
+ export const CONNECT_DATA_EXPAND_DEPTH_LIMIT = 2;
11
+ export const CONNECT_DATA_PAGE_LIMIT_MAX = 1000;
12
+ /**
13
+ * Backend tanımı null alanlarıyla yazar (`field: null`, `children: null`, `rawSql: null`); okunan tanım
14
+ * değiştirilip geri gönderilebilsin diye bu null'lar kabul edilir. `rawSql`/`rawBindings` YALNIZ null
15
+ * olabilir: istemci tanımı ham SQL taşıyamaz (connect-data-filter-invalid).
16
+ */
17
+ const filterSchema = z.lazy(() => z.object({
18
+ filterType: z.enum(['single', 'group']).optional(),
19
+ field: z.string().nullable().optional(),
20
+ operator: z.enum(CONNECT_DATA_OPERATORS).optional(),
21
+ value: z.unknown().optional(),
22
+ conditionType: z.enum(['and', 'or']).optional(),
23
+ children: z.array(filterSchema).nullable().optional(),
24
+ isParentRequired: z.boolean().optional(),
25
+ rawSql: z.null().optional(),
26
+ rawBindings: z.null().optional()
27
+ }).strict());
28
+ const sortSchema = z.object({ field: z.string(), ascending: z.boolean(), rawSql: z.null().optional() }).strict();
29
+ const pageSchema = z.object({ mode: z.enum(['offset', 'cursor', 'client']), limit: z.number().int() }).strict();
30
+ const behaviorSchema = z.object({
31
+ filterable: z.array(z.string()).nullable().optional(),
32
+ searchable: z.array(z.string()).nullable().optional(),
33
+ edit: z.object({
34
+ create: z.boolean().optional(),
35
+ update: z.boolean().optional(),
36
+ delete: z.boolean().optional(),
37
+ bulk: z.boolean().optional(),
38
+ saveMode: z.string().nullable().optional(),
39
+ editable: z.array(z.string()).nullable().optional(),
40
+ defaults: z.record(z.unknown()).nullable().optional(),
41
+ writeMode: z.enum(['atomic', 'partial']).optional()
42
+ }).strict().nullable().optional(),
43
+ refresh: z.object({ afterWrite: z.string().nullable().optional(), poll: z.number().nullable().optional() }).strict().nullable().optional()
44
+ }).strict();
45
+ export const lookupSchema = z.lazy(() => z.object({
46
+ fields: z.array(fieldSchema).nullable().optional(),
47
+ behavior: behaviorSchema.nullable().optional(),
48
+ sort: z.array(sortSchema).nullable().optional(),
49
+ page: pageSchema.nullable().optional(),
50
+ mode: z.string().nullable().optional()
51
+ }).strict());
52
+ const fieldSchema = z.lazy(() => z.object({
53
+ id: z.string().uuid().nullable().optional(),
54
+ slug: z.string(),
55
+ expand: z.array(fieldSchema).nullable().optional(),
56
+ lookup: lookupSchema.nullable().optional()
57
+ }).strict());
58
+ export const connectDataDefinitionSchema = z.object({
59
+ version: z.number().int(),
60
+ source: z.object({ kind: z.enum(CONNECT_DATA_SOURCE_KINDS), id: z.string().uuid(), slug: z.string().nullable().optional() }).strict().nullable(),
61
+ fields: z.array(fieldSchema),
62
+ filter: filterSchema.nullable().optional(),
63
+ sort: z.array(sortSchema).nullable().optional(),
64
+ page: pageSchema.nullable().optional(),
65
+ language: z.string().nullable().optional(),
66
+ mapping: z.object({ roles: z.record(z.string()).nullable().optional(), columns: z.array(z.record(z.unknown())).nullable().optional() }).strict().nullable().optional(),
67
+ behavior: behaviorSchema.nullable().optional(),
68
+ params: z.record(z.unknown()).nullable().optional(),
69
+ expressions: z.record(z.string()).nullable().optional(),
70
+ lookup: lookupSchema.nullable().optional()
71
+ }).strict();
72
+ /** Tanımın seçtiği yollar: `ad`, `sube`, `sube.ad`; expand iki seviyeye kadar. */
73
+ export function selectedPaths(definition) {
74
+ const paths = [];
75
+ const collect = (fields, prefix, depth) => {
76
+ for (const field of fields ?? []) {
77
+ if (!field?.slug?.trim())
78
+ continue;
79
+ const path = prefix === null ? field.slug : `${prefix}.${field.slug}`;
80
+ if (!paths.includes(path))
81
+ paths.push(path);
82
+ if (depth < CONNECT_DATA_EXPAND_DEPTH_LIMIT)
83
+ collect(field.expand, path, depth + 1);
84
+ }
85
+ };
86
+ collect(definition.fields, null, 0);
87
+ return paths;
88
+ }
89
+ /**
90
+ * Backend `ConnectDataDefinitionValidator`'ın kuralları (aynı `rule` adları). Önce zod şekli: şekil
91
+ * düşerse kural kontrolü koşmaz — anahtar adı yanlış bir tanımda alt küme hatası yanıltıcı olurdu.
92
+ */
93
+ export function connectDataShapeIssues(ref, path = 'ref') {
94
+ const parsed = connectDataDefinitionSchema.safeParse(ref);
95
+ if (!parsed.success) {
96
+ return parsed.error.issues.map(issue => ({
97
+ code: 'PAGE-035',
98
+ rule: 'connect-data-definition-invalid',
99
+ path: [path, ...issue.path].join('.'),
100
+ message: issue.message
101
+ }));
102
+ }
103
+ const definition = parsed.data;
104
+ const issues = [];
105
+ const shape = (rule, issuePath, message) => { issues.push({ code: 'PAGE-035', rule, path: issuePath, message }); };
106
+ const subset = (rule, issuePath, message) => { issues.push({ code: 'CD-004', rule, path: issuePath, message }); };
107
+ const paths = selectedPaths(definition);
108
+ const roots = [...new Set(definition.fields.map(field => field.slug).filter(slug => slug?.trim()))];
109
+ if (definition.version !== 1)
110
+ shape('connect-data-version-unsupported', `${path}.version`, `Tanım sürümü ${definition.version} desteklenmiyor; version: 1 yazın.`);
111
+ if (!definition.source)
112
+ shape('connect-data-source-missing', `${path}.source`, "Tanımda kaynak yok: source { kind, id } verin (kaynaklar connect_data_sources ile listelenir).");
113
+ if (definition.fields.length === 0 || definition.fields.some(field => !field.slug?.trim()))
114
+ shape('connect-data-field-missing', `${path}.fields`, 'En az bir alan seçin; her alan slug taşır.');
115
+ const seen = new Set();
116
+ for (const field of definition.fields) {
117
+ if (field.slug && seen.has(field.slug))
118
+ shape('connect-data-field-duplicate', `${path}.fields`, `'${field.slug}' birden fazla seçilmiş.`);
119
+ seen.add(field.slug);
120
+ }
121
+ const checkExpand = (fields, fieldPath, depth) => {
122
+ for (const field of fields ?? []) {
123
+ if (field.lookup?.fields?.length)
124
+ checkExpand(field.lookup.fields, `${fieldPath}.${field.slug}.lookup.fields`, depth + 1);
125
+ if (!field.expand?.length)
126
+ continue;
127
+ if (depth >= CONNECT_DATA_EXPAND_DEPTH_LIMIT) {
128
+ subset('connect-data-expand-too-deep', `${fieldPath}.${field.slug}`, `'${field.slug}' altındaki expand ${CONNECT_DATA_EXPAND_DEPTH_LIMIT} seviyeyi aşıyor.`);
129
+ continue;
130
+ }
131
+ checkExpand(field.expand, `${fieldPath}.${field.slug}`, depth + 1);
132
+ }
133
+ };
134
+ checkExpand(definition.fields, `${path}.fields`, 0);
135
+ const checkFilter = (filter, filterPath) => {
136
+ if (!filter)
137
+ return;
138
+ if (filter.filterType === 'group') {
139
+ if (!filter.children?.length)
140
+ shape('connect-data-filter-invalid', filterPath, 'Filtre grubu en az bir çocuk ister.');
141
+ filter.children?.forEach((child, index) => checkFilter(child, `${filterPath}.children.${index}`));
142
+ return;
143
+ }
144
+ if (!filter.field?.trim())
145
+ shape('connect-data-filter-invalid', filterPath, 'Tekil filtre field ister.');
146
+ const isParameter = typeof filter.value === 'string' && filter.value.startsWith('$');
147
+ if ((filter.operator === 'in' || filter.operator === 'notin') && !Array.isArray(filter.value) && !isParameter) {
148
+ shape('connect-data-filter-invalid', filterPath, `'${filter.field}' in/notin kullanıyor; değer liste ya da $parametre olmalı.`);
149
+ }
150
+ };
151
+ checkFilter(definition.filter, `${path}.filter`);
152
+ for (const sort of definition.sort ?? []) {
153
+ if (!paths.includes(sort.field))
154
+ subset('connect-data-sort-not-selected', `${path}.sort`, `Sıralama alanı '${sort.field}' seçili değil. Seçili: ${paths.join(' · ')}`);
155
+ }
156
+ if ((definition.sort ?? []).length > 1) {
157
+ shape('connect-data-sort-single-key', `${path}.sort`, `Motor tek sıralama anahtarı okur: ${(definition.sort ?? []).length} anahtar verildi, runtime 422 ConnectDataFilterNotAllowed 'sort' döner.`);
158
+ }
159
+ if (definition.page && (definition.page.limit < 1 || definition.page.limit > CONNECT_DATA_PAGE_LIMIT_MAX)) {
160
+ shape('connect-data-page-limit', `${path}.page.limit`, `page.limit 1…${CONNECT_DATA_PAGE_LIMIT_MAX} arasında olmalı.`);
161
+ }
162
+ if (definition.language && !/^(request|default|[a-z]{2})$/.test(definition.language)) {
163
+ shape('connect-data-enum-invalid', `${path}.language`, `language '${definition.language}' geçersiz: request · default · iki harfli dil kodu.`);
164
+ }
165
+ const behavior = definition.behavior;
166
+ for (const item of [...new Set([...(behavior?.filterable ?? []), ...(behavior?.searchable ?? [])])].filter(entry => !paths.includes(entry))) {
167
+ subset('connect-data-filterable-not-selected', `${path}.behavior`, `'${item}' filtrelenebilir/aranabilir ama seçili değil. Seçili: ${paths.join(' · ')}`);
168
+ }
169
+ for (const editable of behavior?.edit?.editable ?? []) {
170
+ if (!roots.includes(editable))
171
+ subset('connect-data-editable-not-selected', `${path}.behavior.edit.editable`, `'${editable}' düzenlenebilir ama seçili bir KÖK alan değil (expand yolu yazılamaz).`);
172
+ }
173
+ const kind = definition.source?.kind;
174
+ if (kind === 'query' || kind === 'view' || kind === 'workflow' || kind === 'microservice') {
175
+ const filtersOnServer = kind === 'microservice';
176
+ const unsupported = [
177
+ ...(definition.filter && !filtersOnServer ? ['filter'] : []),
178
+ ...(definition.sort?.length && !filtersOnServer ? ['sort'] : []),
179
+ ...(behavior?.filterable?.length && !filtersOnServer ? ['behavior.filterable'] : []),
180
+ ...(behavior?.searchable?.length && !filtersOnServer ? ['behavior.searchable'] : []),
181
+ ...(behavior?.edit?.create || behavior?.edit?.update || behavior?.edit?.delete ? ['behavior.edit'] : []),
182
+ ...(definition.page?.mode === 'cursor' ? ['page.mode'] : []),
183
+ ...definition.fields.filter(field => field.expand?.length).map(field => `fields.${field.slug}.expand`),
184
+ ...definition.fields.filter(field => field.lookup).map(field => `fields.${field.slug}.lookup`),
185
+ ...(definition.lookup ? ['lookup'] : [])
186
+ ];
187
+ for (const feature of unsupported) {
188
+ const searchHint = feature === 'behavior.searchable' && kind === 'view'
189
+ ? ' Arama view kaynağında searchable ile değil, istekteki search ile sunucuda çalışır (describe source.textSearch).'
190
+ : '';
191
+ issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.${feature}`, message: `${kind} kaynağında '${feature}' yok: bu kaynak tanımda ${filtersOnServer ? 'yazma, expand ve lookup' : 'filtre, sıralama, searchable, yazma, expand ve lookup'} taşımaz${feature === 'page.mode' ? '; cursor sayfalama yalnız entity kaynağında' : ''}.${searchHint}` });
192
+ }
193
+ }
194
+ if (Object.keys(definition.params ?? {}).length && (kind === 'entity' || kind === 'view')) {
195
+ issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.params`, message: `${kind} kaynağı parametre almaz: params yalnız query, workflow ve microservice kaynağında; entity/view için filter değerinde $ad kullanın.` });
196
+ }
197
+ for (const [name, value] of Object.entries(definition.params ?? {})) {
198
+ if (typeof value === 'string' && value.length > 1 && value.startsWith('$') && !/^\$[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
199
+ shape('connect-data-definition-invalid', `${path}.params.${name}`, `params.${name} = '${value}' sayfa durumu referansı değil: $ ardından harf/rakam/_ ile değişken adı (rakamla başlamaz).`);
200
+ }
201
+ }
202
+ if (behavior?.edit?.saveMode && !['row', 'cell', 'batch'].includes(behavior.edit.saveMode))
203
+ shape('connect-data-enum-invalid', `${path}.behavior.edit.saveMode`, "saveMode yalnız 'row', 'cell' ya da 'batch'.");
204
+ if (behavior?.refresh?.afterWrite && !['reload', 'merge'].includes(behavior.refresh.afterWrite))
205
+ shape('connect-data-enum-invalid', `${path}.behavior.refresh.afterWrite`, "afterWrite yalnız 'reload' ya da 'merge'.");
206
+ for (const [role, fieldPath] of Object.entries(definition.mapping?.roles ?? {})) {
207
+ if (fieldPath?.trim() && !paths.includes(fieldPath))
208
+ subset('connect-data-role-not-selected', `${path}.mapping.roles.${role}`, `'${role}' rolü seçili olmayan '${fieldPath}' alanına eşlenmiş.`);
209
+ }
210
+ const lookups = [
211
+ ...definition.fields.filter(field => field.lookup).map(field => ({ lookup: field.lookup, lookupPath: `${path}.fields.${field.slug}.lookup`, owner: field.slug })),
212
+ ...(definition.lookup ? [{ lookup: definition.lookup, lookupPath: `${path}.lookup`, owner: definition.source?.slug ?? 'kaynak' }] : [])
213
+ ];
214
+ for (const { lookup, lookupPath, owner } of lookups) {
215
+ if ((lookup.fields ?? []).some(item => item.lookup))
216
+ issues.push({ code: 'CD-011', rule: 'connect-data-lookup-nested', path: lookupPath, message: `'${owner}' lookup'ı içinde başka lookup olamaz.` });
217
+ if (lookup.mode && !['drawer', 'modal'].includes(lookup.mode))
218
+ shape('connect-data-enum-invalid', `${lookupPath}.mode`, "lookup.mode yalnız 'drawer' ya da 'modal'.");
219
+ if (lookup.page && (lookup.page.limit < 1 || lookup.page.limit > CONNECT_DATA_PAGE_LIMIT_MAX))
220
+ shape('connect-data-page-limit', `${lookupPath}.page.limit`, `lookup.page.limit 1…${CONNECT_DATA_PAGE_LIMIT_MAX} arasında olmalı.`);
221
+ }
222
+ return issues;
223
+ }
224
+ /** İndeksli prop yolunun joker hâli: `series.1.data` → `series[].data`. */
225
+ export function toWildcardPath(propPath) {
226
+ const segments = [];
227
+ for (const segment of propPath.split('.')) {
228
+ if (/^\d+$/.test(segment) && segments.length > 0)
229
+ segments[segments.length - 1] = `${segments[segments.length - 1]}[]`;
230
+ else
231
+ segments.push(segment);
232
+ }
233
+ return segments.join('.');
234
+ }
235
+ /**
236
+ * Şemada veri kaynağına bağlı prop yolları: `<elementId>.<yol>` — kök prop (`tbl1.rows`) ve statik nesne/dizi öğesi
237
+ * içindeki bağlı prop (`chart1.series.1.data`, D-22).
238
+ */
239
+ export function connectedPropKeys(nodes, into = new Set()) {
240
+ const collect = (elementId, value, path) => {
241
+ if (value?.type === 'connectData') {
242
+ into.add(`${elementId}.${path}`);
243
+ return;
244
+ }
245
+ if (value?.type !== 'static' || !value.data || typeof value.data !== 'object')
246
+ return;
247
+ const entries = Array.isArray(value.data)
248
+ ? value.data.flatMap((item, index) => (item && typeof item === 'object' && !Array.isArray(item) ? Object.entries(item).map(([key, child]) => [`${index}.${key}`, child]) : []))
249
+ : Object.entries(value.data);
250
+ for (const [key, child] of entries)
251
+ collect(elementId, child, `${path}.${key}`);
252
+ };
253
+ for (const node of nodes ?? []) {
254
+ const props = node.props && typeof node.props === 'object' ? node.props : {};
255
+ if (typeof node.elementId === 'string') {
256
+ for (const [prop, value] of Object.entries(props))
257
+ collect(node.elementId, value, prop);
258
+ }
259
+ connectedPropKeys(Array.isArray(node.children) ? node.children : [], into);
260
+ }
261
+ return into;
262
+ }
263
+ /**
264
+ * `set_connect_data` / `remove_connect_data` gövde ön kontrolü ve bağlı prop'a `replace_prop` ile tanım
265
+ * yazma reddi (CD-005). Platformun kesin 422'lerinin aynasıdır; istek gitmeden `rejectedOps` üretir.
266
+ * `connectedProps` (`connectedPropKeys`) verilirse bağlantıya bakan kurallar da koşar: bağlı prop'u — ya da içinde
267
+ * bağlı prop taşıyan üst prop'u (`series` ⊃ `series.1.data`) — ezen yazma ve bağlı olmayan prop'ta
268
+ * `remove_connect_data` (platform `prop-not-connected`). Verilmezse (ağaç okunmadı) yalnız gövde denetlenir.
269
+ */
270
+ export function connectDataOpIssues(op, connectedProps = null) {
271
+ const name = String(op.op ?? '');
272
+ const label = `'${String(op.target ?? '?')}.${String(op.path ?? op.prop ?? '?')}'`;
273
+ const key = `${String(op.target ?? '')}.${String(op.prop ?? '')}`;
274
+ const isConnected = connectedProps?.has(key) ?? false;
275
+ const holdsConnected = [...(connectedProps ?? [])].some(item => item.startsWith(`${key}.`));
276
+ const overwrite = { code: 'CD-005', message: `${label} veri kaynağına bağlı${holdsConnected && !isConnected ? ' bir prop taşıyor (iç içe yol)' : ''}: set_binding / set_prop_type / sarmallı replace_prop bağlantıyı ezer (connect-data-prop-connected). Önce remove_connect_data; yalnız örnek satırları değiştirmek için replace_prop { value: [ham satırlar] }.` };
277
+ if (name === 'replace_prop') {
278
+ const value = op.value;
279
+ const isWrapped = Boolean(value) && typeof value === 'object' && !Array.isArray(value) && typeof value?.type === 'string';
280
+ if (isWrapped && value?.type === 'connectData') {
281
+ return [{ code: 'CD-005', message: `${label}: connectData değeri replace_prop ile yazılmaz — set_connect_data { target, path, ref } kullanın; bağlantıyı kaldırmak için remove_connect_data.` }];
282
+ }
283
+ return (isWrapped && isConnected) || holdsConnected ? [overwrite] : [];
284
+ }
285
+ if ((name === 'set_binding' || name === 'set_prop_type') && (isConnected || holdsConnected))
286
+ return [overwrite];
287
+ if (name !== 'set_connect_data' && name !== 'remove_connect_data')
288
+ return [];
289
+ const issues = [];
290
+ if (name === 'remove_connect_data' && connectedProps && typeof op.target === 'string' && typeof op.path === 'string' && !connectedProps.has(`${op.target}.${op.path}`)) {
291
+ issues.push({ code: 'PAGE-035', message: `${label} veri kaynağına bağlı değil; kaldırılacak bağlantı yok (prop-not-connected). Bağlı prop'lar connect_data_get ya da page_schema_get ile görülür.` });
292
+ }
293
+ if (typeof op.target !== 'string' || !op.target)
294
+ issues.push({ code: 'PAGE-035', message: `${name}: 'target' (elementId) zorunlu.` });
295
+ if (typeof op.path !== 'string' || !op.path)
296
+ issues.push({ code: 'PAGE-035', message: `${name}: 'path' zorunlu — düz prop ('rows'), nesne içi ('config.series') ya da dizi öğesi ('series.1.data').` });
297
+ if (name === 'set_connect_data') {
298
+ const reference = op.ref;
299
+ const isMicroserviceReference = Boolean(reference) && typeof reference === 'object' && !Array.isArray(reference) && reference?.source === undefined && reference?.microserviceId !== undefined;
300
+ if (isMicroserviceReference) {
301
+ // Platform kısa biçimi ÜÇ şartla tanır (`ConnectDataDefinitionModel.ReferencedMicroserviceId`):
302
+ // tam iki alan, `version` VAR (sayı ya da metin) ve `microserviceId` GUID olarak ayrıştırılabiliyor.
303
+ // Guard bunu birebir aynalar; aksi hâlde platform tam tanım dalına düşüp anlamsız bir 'missing-ref' verir.
304
+ const keys = Object.keys(reference ?? {}).filter(key => key !== 'version' && key !== 'microserviceId');
305
+ const guid = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
306
+ if (typeof reference?.microserviceId !== 'string' || !guid.test(reference.microserviceId))
307
+ issues.push({ code: 'PAGE-035', message: `${label}.ref.microserviceId: microservice'e çevrilmiş prop'un kısa biçiminde 'microserviceId' bir GUID metni olmalı (connect-data-definition-invalid).` });
308
+ if (reference?.version === undefined || reference.version === null)
309
+ issues.push({ code: 'PAGE-035', message: `${label}.ref.version: kısa biçim 'version' alanını taşımak zorunda (connect-data-definition-invalid).` });
310
+ else if (typeof reference.version !== 'number' && typeof reference.version !== 'string')
311
+ issues.push({ code: 'PAGE-035', message: `${label}.ref.version: kısa biçimde 'version' sayı ya da metin olmalı (connect-data-definition-invalid).` });
312
+ if (keys.length > 0)
313
+ issues.push({ code: 'PAGE-035', message: `${label}.ref: microservice kısa biçimi yalnız 'version' ve 'microserviceId' taşır; fazladan alan: ${keys.join(', ')} (connect-data-definition-invalid).` });
314
+ }
315
+ else {
316
+ for (const issue of connectDataShapeIssues(op.ref, `${label}.ref`))
317
+ issues.push({ code: issue.code, message: `${issue.path}: ${issue.message} (${issue.rule})` });
318
+ }
319
+ }
320
+ return issues;
321
+ }
322
+ /**
323
+ * CD-005'in akış ayağı: setter adımı veri kaynağına bağlı prop'a yazamaz. Runtime setter bağlı prop'u
324
+ * atlar (flow `setNodeProperty` guard'ı, D-17) — adım koşar, hata vermez, bileşene hiçbir şey yazılmaz.
325
+ * Barındırıcının şeması okunduğu için istek gitmeden bloklanır; şema okunamadıysa sessiz kalır.
326
+ */
327
+ export function connectedSetterFindings(steps, errorSteps, hostNodes) {
328
+ const connected = connectedPropKeys(Array.isArray(hostNodes) ? hostNodes : []);
329
+ if (connected.size === 0)
330
+ return [];
331
+ const findings = [];
332
+ const asSteps = (value) => (Array.isArray(value) ? value : []).filter((step) => Boolean(step) && typeof step === 'object' && step.type === 'setter');
333
+ for (const step of [...asSteps(steps), ...asSteps(errorSteps)]) {
334
+ const iteratees = (step.args ?? {}).iteratees;
335
+ for (const iteratee of Array.isArray(iteratees) ? iteratees : []) {
336
+ const element = typeof iteratee.element === 'string' ? iteratee.element : '';
337
+ const property = typeof iteratee.property === 'string' ? iteratee.property.split('.')[0] ?? '' : '';
338
+ if (!element || !property || !connected.has(`${element}.${property}`))
339
+ continue;
340
+ findings.push({ ruleId: 'CD-005', severity: 'block', message: `'${String(step.name ?? step.stepId)}' setter adımı '${element}.${property}' prop'una yazıyor ama prop veri kaynağına bağlı: runtime setter'ı atlar ve bileşene hiçbir şey yazılmaz. Satırları bağlantı okur; setter'ı kaldırın ya da önce remove_connect_data ile bağlantıyı kaldırın (platform_guide('page') §Kural 1c-3).` });
341
+ }
342
+ }
343
+ return findings;
344
+ }
@@ -0,0 +1,93 @@
1
+ export const CONNECT_DATA_WRITE_OPS = ['create', 'update', 'delete'];
2
+ export const CONNECT_DATA_EXECUTE_OPS = ['list', 'describe', 'lookup', ...CONNECT_DATA_WRITE_OPS];
3
+ /** Tek istekteki satır/kimlik tavanı (b-22): gateway bölmez, bileşen de bölmez. */
4
+ export const CONNECT_DATA_WRITE_ROW_LIMIT = 1000;
5
+ const LOCALIZATIONS_KEY = 'localizations';
6
+ /**
7
+ * `connect_data_execute`'un istek gitmeden bildirdiği CD-012 bulguları: motorun kesin reddedeceği yazma
8
+ * istekleri (tanımda kapalı op → 403, bulk kapalıyken çok satır → 403, editable dışı ya da expand alanı → 422,
9
+ * anahtarsız update → 422, tanım atomikken `mode: partial` → 422) ve boş gövde. Kısmi kipte (`mode` ya da tanımın
10
+ * `writeMode`'u `partial`) anahtarsız satır isteği düşürmez, satır hatası olur → warn. Okuma op'larında bulgu yok; lookup `field` ister.
11
+ * `keyField` kaynağın birincil anahtarıdır (describe `source.primaryKey`); motor aynı kuralları o alanla uygular.
12
+ * `capability` verilirse describe'a dayanan kesin retler de bildirilir: sistem kaynağı (403), dosya alanı (422) ve dizi
13
+ * olmayan m2m değeri (atomic 422, kısmi kipte satır hatası → warn).
14
+ */
15
+ export function executeFindings(input, definition, keyField = 'id', capability) {
16
+ const findings = [];
17
+ const block = (message) => { findings.push({ ruleId: 'CD-012', severity: 'block', message }); };
18
+ const warn = (message) => { findings.push({ ruleId: 'CD-012', severity: 'warn', message }); };
19
+ if (input.op === 'lookup' && !input.field && !definition?.lookup)
20
+ block("op: 'lookup' için 'field' (seçili ilişki alanının slug'ı) gerekli; alansız lookup yalnız tanımın kök lookup'ı açıkken (seçim bileşeni, kendi kaynak) çalışır — platform yoksa 422 ConnectDataLookupOff döner. Açmak için connect_data_bind { lookup: { fields: [] } }.");
21
+ if (!CONNECT_DATA_WRITE_OPS.includes(input.op))
22
+ return findings;
23
+ const op = input.op;
24
+ const edit = (definition?.behavior?.edit) ?? {};
25
+ const count = op === 'delete' ? (input.ids?.length ?? 0) : (input.rows?.length ?? 0);
26
+ const bulkCount = Math.max(input.rows?.length ?? 0, input.ids?.length ?? 0);
27
+ if (edit[op] !== true)
28
+ block(`Tanımda edit.${op} kapalı: platform ConnectDataWriteDisabled (403) döner. Açmak için connect_data_bind { edit: { ${op}: true } }.`);
29
+ if (count === 0)
30
+ block(`op: '${op}' için ${op === 'delete' ? 'ids' : 'rows'} boş.`);
31
+ if (bulkCount > 1 && edit.bulk !== true)
32
+ block(`Tanımda edit.bulk kapalı ama ${bulkCount} satır/kimlik gönderiliyor: platform 403 döner. Tek tek gönderin ya da connect_data_bind { edit: { bulk: true } }.`);
33
+ if (input.mode === 'partial' && edit.writeMode !== 'partial')
34
+ block("Tanımın edit.writeMode'u atomic ama istek mode: 'partial': platform ConnectDataWriteModeNotAllowed (422) döner. Kısmi yazma için connect_data_bind { edit: { writeMode: 'partial' } }.");
35
+ const isPartial = edit.writeMode === 'partial' && input.mode !== 'atomic';
36
+ const fields = capability?.fields ?? [];
37
+ if (capability?.source?.isSystem === true)
38
+ block("Kaynak platformun yönettiği bir sistem tablosu: bağlantı ucu yazmaz, platform ConnectDataWriteDisabled (403) döner.");
39
+ if ((input.rows?.length ?? 0) > 0) {
40
+ const writable = new Set([...(edit.editable ?? []), keyField]);
41
+ const outside = [...new Set((input.rows ?? []).flatMap(writtenKeys).filter(key => key.includes('.') || !writable.has(key)))];
42
+ if (outside.length > 0) {
43
+ block(`Tanımın edit.editable listesinde olmayan alan yazılamaz (expand yolu da yazılamaz): ${outside.join(', ')} — platform ConnectDataFieldNotEditable (422). Editable: ${(edit.editable ?? []).join(' · ') || '(boş)'}.`);
44
+ }
45
+ const files = [...new Set((input.rows ?? []).flatMap(writtenKeys).filter(key => fields.some(field => field.slug === key && (field.type === 'file' || !!field.file))))];
46
+ if (files.length > 0)
47
+ block(`Dosya alanı bağlantı ucundan yazılmaz: ${files.join(', ')} — platform ConnectDataFieldNotEditable (422). Dosyayı file_upload ile yükleyip satırı row_update ile güncelleyin.`);
48
+ const notListIn = (slugs) => (input.rows ?? []).findIndex(row => Object.entries(row ?? {}).some(([key, value]) => slugs.has(key) && value !== null && value !== undefined && !Array.isArray(value)));
49
+ const manyToMany = new Set(fields.filter(field => field.relation?.kind === 'm2m' && !field.slug.includes('.')).map(field => field.slug));
50
+ // Çoklu seçim enum'u ilişki değildir (relation yok) ama motor onu da diziyle yazar; dize 422 BulkRowFailed alır (39/M-16).
51
+ const multiValued = new Set(fields.filter(field => field.isMultiple === true && !field.relation && field.type !== 'file' && !field.file && !field.slug.includes('.')).map(field => field.slug));
52
+ const notList = notListIn(manyToMany);
53
+ const notMulti = notListIn(multiValued);
54
+ if (notList >= 0 && isPartial)
55
+ warn(`satır ${notList}: m2m alanı dizi değil — kısmi kipte o satır errors[]'a düşer. m2m değeri ['<id>', …] tam listedir.`);
56
+ else if (notList >= 0)
57
+ block(`satır ${notList}: m2m alanı dizi değil — platform BulkRowFailed (422, rowIndex ${notList}) döner. m2m değeri ['<id>', …] tam listedir.`);
58
+ if (notMulti >= 0 && isPartial)
59
+ warn(`satır ${notMulti}: çoklu değerli alan dizi değil — kısmi kipte o satır errors[]'a düşer. Çoklu seçim enum'u ['<değer>', …] tam listedir.`);
60
+ else if (notMulti >= 0)
61
+ block(`satır ${notMulti}: çoklu değerli alan dizi değil — platform BulkRowFailed (422, rowIndex ${notMulti}) döner. Çoklu seçim enum'u ['<değer>', …] tam listedir.`);
62
+ }
63
+ if (op === 'update') {
64
+ const keyless = (input.rows ?? []).findIndex(row => row?.[keyField] === undefined || row?.[keyField] === null);
65
+ if (keyless >= 0 && isPartial)
66
+ warn(`update satırı ${keyless} anahtar alanı '${keyField}' taşımıyor: kısmi kipte o satır errors[]'a düşer, diğerleri yazılır.`);
67
+ else if (keyless >= 0)
68
+ block(`update satırı ${keyless} anahtar alanı '${keyField}' taşımıyor: platform BulkRowFailed (422, rowIndex ${keyless}) döner.`);
69
+ }
70
+ return findings;
71
+ }
72
+ /** Satırın yazdığı alan adları: kök anahtarlar ve `localizations` içindeki her dilin alanları (motor ikisini de editable'a karşı denetler). */
73
+ function writtenKeys(row) {
74
+ const { [LOCALIZATIONS_KEY]: localizations, ...values } = row ?? {};
75
+ const languages = localizations && typeof localizations === 'object' ? Object.values(localizations) : [];
76
+ return [...Object.keys(values), ...languages.flatMap(language => language && typeof language === 'object' ? Object.keys(language) : [])];
77
+ }
78
+ /** Satırın kısa özeti: anahtar varsa `id=…`, yoksa ilk alan. */
79
+ export function rowSummary(row, keyField = 'id') {
80
+ if (!row)
81
+ return undefined;
82
+ if (row[keyField] !== undefined && row[keyField] !== null)
83
+ return `${keyField}=${String(row[keyField])}`;
84
+ const [first] = Object.entries(row);
85
+ return first ? `${first[0]}=${typeof first[1] === 'object' ? JSON.stringify(first[1]) : String(first[1])}` : undefined;
86
+ }
87
+ /** Yanıtın `errors[]` öğelerine (kısmi yazma, b-22) gönderilen satırın özeti eklenir. */
88
+ export function enrichRowErrors(errors, rows, keyField = 'id') {
89
+ return (errors ?? []).map(error => {
90
+ const summary = rowSummary(rows?.[error.row], keyField);
91
+ return summary ? { ...error, rowSummary: summary } : error;
92
+ });
93
+ }
@@ -0,0 +1,128 @@
1
+ export const connectDataRuleCatalog = [
2
+ {
3
+ id: 'CD-001',
4
+ severity: 'block',
5
+ rule: "Bağlama yalnız bileşenin bağlanabilir prop'una yapılır (component_get.connectData.props; joker series[].data ↔ series.1.data) ve zorunlu roller eşlenir.",
6
+ enforcedBy: 'connect_data_bind — connectDataBind.ts buildDefinition',
7
+ source: 'Katalog definition.connectData (b-16); PageSchemaValidator connect-data-prop-not-connectable'
8
+ },
9
+ {
10
+ id: 'CD-002',
11
+ severity: 'block',
12
+ rule: "Filtrede alan süzülebilir ve işleç describe listesinde olmalı; runtime/önizleme filtresinde alan behavior.filterable'da olmalı. Korumalı alanda yalnız eq · neq · in · notin.",
13
+ enforcedBy: 'connect_data_preview ve connect_data_execute (istek filtresi; lookup filtresi hedefe uygulandığı için hariç), connect_data_bind (filterable/searchable, tanım filtresi) — filterFindings',
14
+ source: 'ConnectDataService EnsureFilterAllowed → ConnectDataFilterNotAllowedException (422); ConnectDataCapabilityResolver ProtectedOperators'
15
+ },
16
+ {
17
+ id: 'CD-003',
18
+ severity: 'block',
19
+ rule: 'Sistem tablosunda ekleme, güncelleme ve silme anahtarları açılamaz (platform yalnız bu üçünü reddeder).',
20
+ enforcedBy: 'connect_data_bind — buildDefinition',
21
+ source: 'Defaults.SystemManagedEntityIds; describe source.isSystem'
22
+ },
23
+ {
24
+ id: 'CD-004',
25
+ severity: 'block',
26
+ rule: 'Davranış, rol ve sıralama seçili alanların alt kümesidir; editable yalnız seçili kök alan; expand ≤ 2 seviye.',
27
+ enforcedBy: 'set_connect_data op gövdesi (page_schema_patch, layout/component yamaları) — connectDataShapeIssues; connect_data_bind',
28
+ source: 'ConnectDataDefinitionValidator connect-data-*-not-selected, connect-data-expand-too-deep'
29
+ },
30
+ {
31
+ id: 'CD-005',
32
+ severity: 'block',
33
+ rule: "Bağlı prop'a set_binding, set_prop_type, sarmallı replace_prop ya da flow setter yazılmaz.",
34
+ enforcedBy: "page_schema_patch — connectDataOpIssues; flow_create · flow_update · flow_patch — connectedSetterFindings",
35
+ source: 'PageSchemaMicroOpApplier connect-data-prop-connected; flow setNodeProperty guard (D-17)'
36
+ },
37
+ {
38
+ id: 'CD-006',
39
+ severity: 'warn',
40
+ rule: "Tanımda açık düzenleme eylemine ya da edit.editable alanına politika izin vermiyorsa ayar etkisizdir (runtime 403).",
41
+ enforcedBy: 'connect_data_bind — buildDefinition (gateway kimliği) ve asUserId verilirse o kullanıcının gözüyle userEyeFindings (38/M-13); connect_data_get (asUserId ile o kullanıcının gözüyle) — editFindings; Studio çekmecesi policyBlockedActions',
42
+ source: 'ConnectDataCapabilityResolver write { create, update, delete, bulk }'
43
+ },
44
+ {
45
+ id: 'CD-007',
46
+ severity: 'warn',
47
+ rule: "page.mode 'client' kaynakta (sayfalama parametresi olmayan query, workflow, limit parametresi olmayan microservice) sunucu sayfalamaz: runtime satırları tek okumada (en çok 1000) alır, süzme/arama/sayfalama tarayıcıda (f-33); 1000'den büyük sonuç eksik görünür.",
48
+ enforcedBy: 'connect_data_bind — buildDefinition (describe source.pageMode)',
49
+ source: 'b-31/b-32 ConnectDataSourceMetaModel.PageMode; ConnectDataPageMode.Client'
50
+ },
51
+ {
52
+ id: 'CD-008',
53
+ severity: 'warn',
54
+ rule: "Kaynaktan silinmiş alan tanımda kalırsa uyarı; connect_data_bind fixDrift: true ile düşürür. Kimliksiz (yalnız slug'lı) tanım alanı kaynakta yoksa da drift { fieldId: null, was: <slug>, removed: true } gelir (39/03 U-10). Yeniden adlanan alan list/preview yanıtının meta.drift'inde de { was, now } olarak bildirilir (39/03 U-9).",
55
+ enforcedBy: 'connect_data_bind — var olan bağlantıda kayıtlı tanımla describe edilir, buildDefinition uyarır; connect_data_get drift[]',
56
+ source: 'ConnectDataCapabilityResolver drift (fieldId ile)'
57
+ },
58
+ {
59
+ id: 'CD-009',
60
+ severity: 'warn',
61
+ rule: "Ağaç bileşeni (children rolü = üst kayıt alanı) tüm satırları tek istekte okur, tavan 1000; daha büyük tabloda ağaç eksik çizilir.",
62
+ enforcedBy: 'connect_data_bind — buildDefinition (roles.children varsa warn)',
63
+ source: 'f-32 treeQueryState / toTree (ng-apps-flow)'
64
+ },
65
+ {
66
+ id: 'CD-010',
67
+ severity: 'warn',
68
+ rule: "Aynı sayfada aynı entity'ye 5+ bağlantı: sayfa açılışında o kadar istek.",
69
+ enforcedBy: 'connect_data_bind — sameSourceFindings (okunan sayfa şemasında aynı source.id taşıyan bağlı proplar, iç içe yol dahil; bu bağlantı bir kez sayılır)',
70
+ source: 'Runtime ConnectDataBinding (bağlantı başına bir istek)'
71
+ },
72
+ {
73
+ id: 'CD-011',
74
+ severity: 'block+warn',
75
+ rule: "fields[].lookup yalnız ilişki alanında, lookup.fields hedefin alanları; tanımın kök lookup'ı (seçim bileşeni, ADR 0007) kaynağın kendi alanlarıyla ve yalnız entity kaynağında; lookup içinde lookup yok (block); hedef okunamıyorsa lookup tanımı etkisiz (warn).",
76
+ enforcedBy: 'set_connect_data op gövdesi — connect-data-lookup-nested (block); lookup.fields hedefin alanları ve ilişki alanı şartı platform 422; hedef okunamıyorsa connect_data_bind — lookupReadabilityFindings (warn; gateway kimliği yönetici olduğundan yalnız asUserId ile o kullanıcının gözünde çıkar, 38/M-13)',
77
+ source: 'ConnectDataDefinitionValidator connect-data-lookup-nested (alan + kök lookup aynı döngü); ConnectDataService.ValidateSourcesAsync connect-data-lookup-not-relation, connect-data-lookup-field-missing (kök lookup: props.<prop>.ref.lookup.fields.<alan>)'
78
+ },
79
+ {
80
+ id: 'CD-012',
81
+ severity: 'block',
82
+ rule: "connect_data_execute isteği motorun kesin reddedeceği şekildeyse gönderilmez: tanımda kapalı op (403), bulk kapalıyken 2+ satır ya da kimlik (403), editable dışı ya da expand alanı — localizations içindeki alanlar dahil (422), kaynağın birincil anahtarını taşımayan update satırı (422; kısmi kipte satır hatası → warn), tanımın writeMode'u atomic iken mode partial (422), boş rows/ids; describe'dan sistem kaynağı (403), dosya alanı (422), dizi olmayan m2m ya da çoklu enum (isMultiple; atomic 422, kısmi warn — 39/M-16); lookup field ister.",
83
+ enforcedBy: 'connect_data_execute — connectDataExecute.executeFindings (istek gönderilmez)',
84
+ source: 'b-21 ConnectDataService.WriteAsync gate sırası'
85
+ },
86
+ {
87
+ id: 'CD-013',
88
+ severity: 'block',
89
+ rule: "query, view ve workflow kaynağında filter, sort, behavior.filterable/searchable, açık edit, expand ve lookup yok; microservice kaynağında edit, expand ve lookup yok (filtre/sıralama/arama uç destekliyorsa describe canFilter/sortable ile açılır; arama yalnız içerir işleçli korumasız metin alanında — dinamik filtreye OR içerir koşulu olarak iner, 2026-10-02). page.mode 'cursor' yalnız entity kaynağında (b-35 A).",
90
+ enforcedBy: 'set_connect_data op gövdesi ve connect_data_bind — connectDataShapeIssues; connect_data_bind params adları describe source.parameters\'a karşı (buildDefinition)',
91
+ source: 'b-31/b-32 ConnectDataDefinitionValidator connect-data-source-feature-unsupported (422); f-33 ConnectDataService.ValidateSourcesAsync ref.params.<ad>'
92
+ },
93
+ {
94
+ id: 'CD-014',
95
+ severity: 'warn',
96
+ rule: "Tanımın filtre değerlerindeki ve params'ındaki $ad sayfa/uygulama değişkenlerinde (page_state) tanımlı olmalı; yoksa runtime null gönderir. Önizleme $ad'ı sayfa durumundan doldurmaz: request.params.<ad> verilmezse null bağlanır ve sonuç sessizce boş döner.",
97
+ enforcedBy: 'connect_data_bind — stateReferenceFindings (GET variables sayfa + uygulama); connect_data_preview — previewStateFindings (request.params içinde karşılığı olmayan $ad; sayfa değişkeni okunabildiyse başlangıç değeri önerilir, 38/M-8)',
98
+ source: 'f-33 ng-apps-flow ConnectDataBinding requestParams (getState page → app)'
99
+ },
100
+ {
101
+ id: 'CD-015',
102
+ severity: 'warn',
103
+ rule: "Rol eşlemesi bileşenin çizim ayarlarını kapsamaz: kategori + değer rolü olan bileşende (grafik ailesi) ham satırlar kategoriyi satır başına tekrar eder; toplama bileşenin kendi 'aggregate' ayarıdır (varsayılan 'none') ve bağlama onu yazmaz.",
104
+ enforcedBy: 'connect_data_bind — buildDefinition (category + value rolü, fieldsFrom olmayan bileşen)',
105
+ source: 'koşu bulgusu S-6 ng-apps-ui chart data-shape mergeSeriesSources/aggregateRows'
106
+ },
107
+ {
108
+ id: 'CD-016',
109
+ severity: 'warn',
110
+ rule: "Rol eşlemesinde alanın tipi rolün tipini besleyebilmeli (component_get.connectData.props.<prop>.roles): metin rolü skaler alan (ilişki/dosya nesnesi değil), sayı/boolean/tarih rolü kendi tipinde alan, üst kayıt rolü anahtar tipinde alan ister. Uymayan eşleme kaydedilir ama bileşen değeri beklediği gibi çizmez.",
111
+ enforcedBy: 'connect_data_bind — buildDefinition (roleTypeMismatch); Studio sihirbaz/çekmece eşleme adımı aynı kuralla uyarır',
112
+ source: 'S9 denetimi B-3 (b-16/b-38 rol tipi uyarısı hiçbir katmanda yoktu; backend doğrulama kanalı yalnız hata taşır)'
113
+ },
114
+ {
115
+ id: 'CD-017',
116
+ severity: 'warn',
117
+ rule: "Anonim sayfada (page isAnonymous) yazmaya açık bağlantı oturumsuz ziyaretçiye yazma verir: backend yazmada kimliği yalnız anonim olmayan sayfada ister. Basit projede her zaman, kompleks projede politika anonim özneye izin veriyorsa yazılır.",
118
+ enforcedBy: 'connect_data_bind — anonymousWriteFindings (sayfa GET isAnonymous ∧ behavior.edit create/update/delete); Studio çekmece/sihirbaz düzenleme bölümü aynı koşulla uyarır',
119
+ source: 'S9 denetimi Y-5 (b-23 sapma 1 f-16 notu); backend ExecuteConnectDataBehavior'
120
+ },
121
+ {
122
+ id: 'CD-018',
123
+ severity: 'block',
124
+ rule: "Dosya alanı (describe type 'file') edit.editable'a konamaz: bağlı bileşen dosyayı satır içinde yazmaz. edit açıkken editable verilmezse gateway'in önerdiği yazılabilir alanlar da dosya alanını dışarıda bırakır.",
125
+ enforcedBy: 'connect_data_bind — buildDefinition (dryRun dahil, istek gitmeden)',
126
+ source: "38/M-4; ConnectDataService.ValidateSourcesAsync ConnectDataRules.EditableFile 'connect-data-editable-file' (422, kayıtta)"
127
+ }
128
+ ];