@appaflytech/zeyrek-ai-mcp 0.0.43 → 0.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/knowledge/backendConstraints.js +86 -2
  2. package/dist/knowledge/componentCatalog.js +63 -26
  3. package/dist/knowledge/componentEvents.js +6 -0
  4. package/dist/knowledge/connectDataBind.js +43 -6
  5. package/dist/knowledge/connectDataDefinition.js +74 -3
  6. package/dist/knowledge/connectDataRules.js +14 -0
  7. package/dist/knowledge/mobileComponentCatalog.js +66 -7
  8. package/dist/knowledge/mobileStore.js +61 -2
  9. package/dist/knowledge/platform/mobile.js +1 -1
  10. package/dist/knowledge/platform/mobileRules.js +2 -1
  11. package/dist/knowledge/platform/mobileTabs.js +31 -2
  12. package/dist/knowledge/platform/web.js +1 -1
  13. package/dist/knowledge/rules.js +195 -4
  14. package/dist/knowledge/textHooks.js +434 -0
  15. package/dist/platform/capture/capture.js +1 -1
  16. package/dist/platform/capture/readiness.js +4 -0
  17. package/dist/platform/errors.js +22 -2
  18. package/dist/prompts/mobilePublish.js +21 -0
  19. package/dist/resources/examples/flow-biometric-confirm.json +199 -0
  20. package/dist/resources/examples/microfunction-node-dependencies.json +19 -0
  21. package/dist/resources/guides/access.md +3 -3
  22. package/dist/resources/guides/component.md +20 -4
  23. package/dist/resources/guides/deployment.md +98 -0
  24. package/dist/resources/guides/entity.md +1 -1
  25. package/dist/resources/guides/expressions.md +5 -1
  26. package/dist/resources/guides/external-component.md +6 -4
  27. package/dist/resources/guides/merge.md +81 -4
  28. package/dist/resources/guides/microfunction.md +104 -9
  29. package/dist/resources/guides/microservice.md +34 -5
  30. package/dist/resources/guides/mobile-connect-data.md +70 -0
  31. package/dist/resources/guides/mobile-flow.md +64 -0
  32. package/dist/resources/guides/mobile-publish.md +161 -16
  33. package/dist/resources/guides/mobile-steps.md +17 -0
  34. package/dist/resources/guides/mobile.md +41 -23
  35. package/dist/resources/guides/page.md +11 -5
  36. package/dist/resources/guides/translation.md +95 -0
  37. package/dist/resources/knowledge.js +3 -1
  38. package/dist/resources/prompts/mobile-publish-prepare.md +89 -0
  39. package/dist/resources/schema-fingerprint.json +4 -4
  40. package/dist/resources/schemas/ApplyMicrofunctionArchiveChangesCommand.json +76 -0
  41. package/dist/resources/schemas/ConnectDataDescribeDto.json +44 -0
  42. package/dist/resources/schemas/ConnectDataResultDto.json +10 -0
  43. package/dist/resources/schemas/CreateMicrofunctionCommand.json +8 -0
  44. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +0 -4
  45. package/dist/resources/schemas/MobileStorePreflightScope.json +13 -0
  46. package/dist/resources/schemas/StartMobileBuildCommand.json +35 -1
  47. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +8 -0
  48. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +75 -0
  49. package/dist/server.js +2 -0
  50. package/dist/tools/common.js +6 -0
  51. package/dist/tools/components.js +19 -6
  52. package/dist/tools/connectData.js +9 -4
  53. package/dist/tools/deployments.js +1 -1
  54. package/dist/tools/designSchemaPatch.js +16 -4
  55. package/dist/tools/environments.js +3 -3
  56. package/dist/tools/files.js +1 -1
  57. package/dist/tools/flows.js +29 -3
  58. package/dist/tools/layouts.js +32 -12
  59. package/dist/tools/merges.js +8 -8
  60. package/dist/tools/microfunctions.js +53 -3
  61. package/dist/tools/mobileStore.js +83 -21
  62. package/dist/tools/pages.js +59 -24
  63. package/dist/tools/platformInfo.js +4 -2
  64. package/dist/tools/textHookGuard.js +116 -0
  65. package/package.json +2 -2
@@ -291,7 +291,7 @@ function unsortableReason(kind) {
291
291
  * Girdi + kabiliyet + bağlanabilir prop kaydından tanım. Block bulgusu varsa tanım yine döner
292
292
  * (dryRun'da görülsün) ama çağıran yazmaz.
293
293
  */
294
- export function buildDefinition(input, describe, connectable, connectablePaths = [], itemProps = []) {
294
+ export function buildDefinition(input, describe, connectable, connectablePaths = [], itemProps = [], platform = 'web') {
295
295
  const findings = [];
296
296
  const block = (ruleId, message) => { findings.push({ ruleId, severity: 'block', message }); };
297
297
  const warn = (ruleId, message) => { findings.push({ ruleId, severity: 'warn', message }); };
@@ -401,9 +401,18 @@ export function buildDefinition(input, describe, connectable, connectablePaths =
401
401
  block('CD-002', `'${slug}' aranamaz: arama yalnız korumasız, içerir (icontains) işleci olan metin alanında çalışır — platform bu alanı aramada YOK SAYAR (geçerli hiç alan kalmazsa 422 ConnectDataFilterNotAllowed). Aranabilir bir metin alanı seçin.`);
402
402
  }
403
403
  }
404
- const rootLookup = input.lookup !== undefined
404
+ const givenLookup = input.lookup !== undefined
405
405
  ? input.lookup
406
406
  : connectable?.lookup && input.source.kind === 'entity' ? { fields: [], mode: 'modal' } : null;
407
+ const isMobile = platform === 'mobile';
408
+ // Telefon satırının varsayılan başlığı: lookup label alanını gösteriyorsa (ya da alanlar türetiliyorsa) o, yoksa ilk alan
409
+ // (screen-builder `defaultMobileRow` aynası).
410
+ const lookupSlugs = (givenLookup?.fields ?? []).map(item => item.slug).filter(slug => slug?.trim());
411
+ const rowTitle = roles.label && (lookupSlugs.length === 0 || lookupSlugs.includes(roles.label)) ? roles.label : lookupSlugs[0];
412
+ const rootLookup = givenLookup && isMobile
413
+ ? { ...givenLookup, mobileMode: givenLookup.mobileMode ?? 'sheet', ...(givenLookup.mobileRow || !rowTitle ? {} : { mobileRow: { title: rowTitle } }) }
414
+ : givenLookup;
415
+ const mobile = input.mobile !== undefined ? input.mobile : isMobile && (isListComponent || rootLookup) ? { infiniteScroll: true } : undefined;
407
416
  const definition = {
408
417
  version: 1,
409
418
  source: { kind: input.source.kind, id: input.source.id, ...(input.source.slug ?? describe.source?.slug ? { slug: input.source.slug ?? describe.source?.slug } : {}) },
@@ -423,11 +432,27 @@ export function buildDefinition(input, describe, connectable, connectablePaths =
423
432
  defaults: input.edit?.defaults ?? {},
424
433
  writeMode: input.edit?.writeMode ?? 'atomic'
425
434
  },
426
- refresh: { afterWrite: input.refresh?.afterWrite ?? 'reload', poll: input.refresh?.poll ?? null }
435
+ refresh: { afterWrite: input.refresh?.afterWrite ?? 'reload', poll: input.refresh?.poll ?? null },
436
+ ...(mobile ? { mobile } : {}),
437
+ ...(input.mobileCard ? { mobileCard: input.mobileCard } : {})
427
438
  },
428
439
  ...(input.params && Object.keys(input.params).length > 0 ? { params: input.params } : {}),
429
- ...(rootLookup ? { lookup: rootLookup } : {})
440
+ ...(rootLookup ? { lookup: rootLookup } : {}),
441
+ ...(input.grouping ? { grouping: input.grouping } : {})
430
442
  };
443
+ const groups = (connectable?.wizardSteps ?? []).includes('grouping');
444
+ if (groups && !input.grouping) {
445
+ block('CD-019', `Bu bileşen satırları bölümlere ayırır: gruplama alanı seçin — grouping: { by: '<alan>', sectionOrder: 'asc' | 'desc' | 'asis', dateBucket?: 'day' | 'week' | 'month' } verin. Sunucu sırayı gruplama alanıyla başlatır; date alanında gün/hafta/ay kovası satıra __section olarak yazılır, datetime alanında cihaz yerel günden kovalar.`);
446
+ }
447
+ if (groups && input.grouping?.dateBucket && !describe.fields.find(item => item.slug === input.grouping?.by)?.dateBuckets?.length) {
448
+ block('CD-019', `grouping.dateBucket '${input.grouping.dateBucket}': '${input.grouping.by}' tarih alanı değil (describe dateBuckets yok) — platform 422 grouping.dateBucket döner. Tarih/tarih-saat alanı seçin ya da dateBucket'ı kaldırın.`);
449
+ }
450
+ if (groups && input.grouping && input.grouping.sectionOrder !== 'asis' && describe.fields.find(item => item.slug === input.grouping?.by)?.sortable === false) {
451
+ block('CD-019', `grouping.by '${input.grouping.by}' bu kaynakta sıralanamaz (describe sortable yok): sunucu gruplu listeyi gruplama alanıyla sıralar — platform kaydı 422 connect-data-grouping-not-sortable döner. Sıralanabilir bir alan seçin ya da sectionOrder: 'asis' verin.`);
452
+ }
453
+ if (!groups && input.grouping) {
454
+ block('CD-020', `grouping yalnız satırları bölümleyen bileşende (component_get.connectData.props.<prop>.wizardSteps 'grouping' — mobilde grouped-list) yazılır; bu bileşen gruplamayı okumaz. grouping'i kaldırın.`);
455
+ }
431
456
  const declaredParameters = (describe.source?.parameters ?? []).map(parameter => parameter.name);
432
457
  for (const name of Object.keys(input.params ?? {}).filter(item => !declaredParameters.some(declared => declared.toLowerCase() === item.toLowerCase()))) {
433
458
  block('CD-013', `params.${name}: '${describe.source?.slug ?? describe.source?.id}' böyle bir parametre almaz. Alınanlar: ${declaredParameters.join(' · ') || '(yok)'} (describe source.parameters; sayfalama, sıralama, dinamik filtre ve dil parametreleri motorundur).`);
@@ -559,6 +584,9 @@ export function inputFromExisting(input, existing) {
559
584
  pageSize: input.pageSize ?? existing.page?.limit,
560
585
  params: input.params ?? (existing.params && Object.keys(existing.params).length > 0 ? { ...existing.params } : undefined),
561
586
  lookup: input.lookup !== undefined ? input.lookup : existing.lookup ?? null,
587
+ mobile: input.mobile !== undefined ? input.mobile : existing.behavior?.mobile ?? undefined,
588
+ mobileCard: input.mobileCard !== undefined ? input.mobileCard : existing.behavior?.mobileCard ?? undefined,
589
+ grouping: input.grouping !== undefined ? input.grouping : existing.grouping ?? undefined,
562
590
  language: input.language ?? existing.language ?? undefined,
563
591
  filterable: input.filterable ?? [...(existing.behavior?.filterable ?? [])],
564
592
  searchable: input.searchable ?? [...(existing.behavior?.searchable ?? [])],
@@ -644,10 +672,12 @@ export function renameDefinitionPaths(definition, renames) {
644
672
  ...(edit.defaults ? { defaults: Object.fromEntries(Object.entries(edit.defaults).map(([path, value]) => [rename(path), value])) } : {})
645
673
  }
646
674
  }
647
- : {})
675
+ : {}),
676
+ ...(definition.behavior.mobileCard ? { mobileCard: renameMobileParts(definition.behavior.mobileCard, rename) } : {})
648
677
  }
649
678
  }
650
679
  : {}),
680
+ ...(definition.grouping ? { grouping: { ...definition.grouping, by: rename(definition.grouping.by) } } : {}),
651
681
  ...(definition.lookup
652
682
  ? {
653
683
  lookup: {
@@ -662,7 +692,8 @@ export function renameDefinitionPaths(definition, renames) {
662
692
  }
663
693
  }
664
694
  : {}),
665
- ...(definition.lookup.sort ? { sort: definition.lookup.sort.map(item => ({ ...item, field: rename(item.field) })) } : {})
695
+ ...(definition.lookup.sort ? { sort: definition.lookup.sort.map(item => ({ ...item, field: rename(item.field) })) } : {}),
696
+ ...(definition.lookup.mobileRow ? { mobileRow: renameMobileParts(definition.lookup.mobileRow, rename) } : {})
666
697
  }
667
698
  }
668
699
  : {})
@@ -706,6 +737,10 @@ export function capabilityUnderRecordedNames(describe) {
706
737
  return Object.keys(recorded).length === 0 ? describe : { ...describe, fields: describe.fields.map(field => ({ ...field, slug: renamePath(field.slug, recorded) })) };
707
738
  }
708
739
  /** Bind girdisindeki yolları (alanlar, roller, expand, filtre, sıralama, filterable/searchable, edit) yeniden adlandırır. */
740
+ /** Telefon satırı/kartı parçalarının (`title`, `subtitle`, `trailing`, `fields`) yollarını çevirir. */
741
+ function renameMobileParts(parts, rename) {
742
+ return Object.fromEntries(Object.entries(parts).map(([part, value]) => [part, typeof value === 'string' ? rename(value) : Array.isArray(value) ? value.map(item => rename(String(item))) : value]));
743
+ }
709
744
  export function renameBindInput(input, renames) {
710
745
  if (Object.keys(renames).length === 0)
711
746
  return input;
@@ -724,6 +759,8 @@ export function renameBindInput(input, renames) {
724
759
  ...(input.sort ? { sort: input.sort.map(item => ({ ...item, field: rename(item.field) })) } : {}),
725
760
  ...(input.filterable ? { filterable: input.filterable.map(rename) } : {}),
726
761
  ...(input.searchable ? { searchable: input.searchable.map(rename) } : {}),
762
+ ...(input.mobileCard ? { mobileCard: renameMobileParts(input.mobileCard, rename) } : {}),
763
+ ...(input.grouping ? { grouping: { ...input.grouping, by: rename(input.grouping.by) } } : {}),
727
764
  ...(input.edit
728
765
  ? {
729
766
  edit: {
@@ -27,6 +27,35 @@ const filterSchema = z.lazy(() => z.object({
27
27
  }).strict());
28
28
  const sortSchema = z.object({ field: z.string(), ascending: z.boolean(), rawSql: z.null().optional() }).strict();
29
29
  const pageSchema = z.object({ mode: z.enum(['offset', 'cursor', 'client']), limit: z.number().int() }).strict();
30
+ /** Telefonda lookup yüzeyi; yoksa `sheet` (backend `ConnectDataDefinitionValidator.LookupMobileModes`). */
31
+ export const CONNECT_DATA_LOOKUP_MOBILE_MODES = ['sheet', 'fullscreen'];
32
+ const mobileRowSchema = z.object({ title: z.string(), subtitle: z.string().nullable().optional(), trailing: z.string().nullable().optional() }).strict();
33
+ /** Backend `ConnectDataMobileBehaviorModel`: telefon "Telefonda" kutusunun anahtarları; sunucu okumaz. */
34
+ export const mobileBehaviorSchema = z.object({
35
+ search: z.boolean().nullable().optional(),
36
+ infiniteScroll: z.boolean().nullable().optional(),
37
+ pullToRefresh: z.boolean().nullable().optional(),
38
+ recentSelections: z.boolean().nullable().optional(),
39
+ filterChips: z.boolean().nullable().optional(),
40
+ swipeDelete: z.boolean().nullable().optional()
41
+ }).strict();
42
+ /** Backend `ConnectDataMobileCardModel`: datatable kart görünümü telefonda (seçili yollar). */
43
+ export const mobileCardSchema = z.object({ title: z.string(), fields: z.array(z.string()).nullable().optional(), trailing: z.string().nullable().optional() }).strict();
44
+ /** Backend `ConnectDataRules.GroupingDateBuckets` / `GroupingSectionOrders` (kök ADR 0019). */
45
+ export const CONNECT_DATA_GROUPING_DATE_BUCKETS = ['day', 'week', 'month'];
46
+ export const CONNECT_DATA_GROUPING_SECTION_ORDERS = ['asc', 'desc', 'asis'];
47
+ /**
48
+ * Backend `ConnectDataGroupingModel`: gruplu listenin bölümlemesi. Sunucu sırayı `by` ile başlatır (`asis` hariç),
49
+ * `dateBucket` varsa `date` alanında satıra `__section` yazar (`datetime` cihazda kovalanır). Değerler metin olarak alınır, kural `connectDataShapeIssues`'ta.
50
+ */
51
+ export const groupingSchema = z.object({
52
+ by: z.string(),
53
+ dateBucket: z.string().nullable().optional(),
54
+ sectionOrder: z.string().nullable().optional(),
55
+ otherSectionTitle: z.string().nullable().optional(),
56
+ stickyHeaders: z.boolean().nullable().optional(),
57
+ showCounts: z.boolean().nullable().optional()
58
+ }).strict();
30
59
  const behaviorSchema = z.object({
31
60
  filterable: z.array(z.string()).nullable().optional(),
32
61
  searchable: z.array(z.string()).nullable().optional(),
@@ -40,14 +69,18 @@ const behaviorSchema = z.object({
40
69
  defaults: z.record(z.unknown()).nullable().optional(),
41
70
  writeMode: z.enum(['atomic', 'partial']).optional()
42
71
  }).strict().nullable().optional(),
43
- refresh: z.object({ afterWrite: z.string().nullable().optional(), poll: z.number().nullable().optional() }).strict().nullable().optional()
72
+ refresh: z.object({ afterWrite: z.string().nullable().optional(), poll: z.number().nullable().optional() }).strict().nullable().optional(),
73
+ mobile: mobileBehaviorSchema.nullable().optional(),
74
+ mobileCard: mobileCardSchema.nullable().optional()
44
75
  }).strict();
45
76
  export const lookupSchema = z.lazy(() => z.object({
46
77
  fields: z.array(fieldSchema).nullable().optional(),
47
78
  behavior: behaviorSchema.nullable().optional(),
48
79
  sort: z.array(sortSchema).nullable().optional(),
49
80
  page: pageSchema.nullable().optional(),
50
- mode: z.string().nullable().optional()
81
+ mode: z.string().nullable().optional(),
82
+ mobileMode: z.string().nullable().optional(),
83
+ mobileRow: mobileRowSchema.nullable().optional()
51
84
  }).strict());
52
85
  const fieldSchema = z.lazy(() => z.object({
53
86
  id: z.string().uuid().nullable().optional(),
@@ -67,7 +100,8 @@ export const connectDataDefinitionSchema = z.object({
67
100
  behavior: behaviorSchema.nullable().optional(),
68
101
  params: z.record(z.unknown()).nullable().optional(),
69
102
  expressions: z.record(z.string()).nullable().optional(),
70
- lookup: lookupSchema.nullable().optional()
103
+ lookup: lookupSchema.nullable().optional(),
104
+ grouping: groupingSchema.nullable().optional()
71
105
  }).strict();
72
106
  /** Tanımın seçtiği yollar: `ad`, `sube`, `sube.ad`; expand iki seviyeye kadar. */
73
107
  export function selectedPaths(definition) {
@@ -203,6 +237,31 @@ export function connectDataShapeIssues(ref, path = 'ref') {
203
237
  shape('connect-data-enum-invalid', `${path}.behavior.edit.saveMode`, "saveMode yalnız 'row', 'cell' ya da 'batch'.");
204
238
  if (behavior?.refresh?.afterWrite && !['reload', 'merge'].includes(behavior.refresh.afterWrite))
205
239
  shape('connect-data-enum-invalid', `${path}.behavior.refresh.afterWrite`, "afterWrite yalnız 'reload' ya da 'merge'.");
240
+ const mobileCard = behavior?.mobileCard;
241
+ if (mobileCard) {
242
+ const cardParts = [['title', mobileCard.title ?? ''], ...(mobileCard.fields ?? []).map((field) => ['fields', field]), ...(mobileCard.trailing != null ? [['trailing', mobileCard.trailing]] : [])];
243
+ const reported = new Set();
244
+ for (const [part, field] of cardParts.filter(([, field]) => !paths.includes(field))) {
245
+ if (reported.has(part))
246
+ continue;
247
+ reported.add(part);
248
+ subset('connect-data-mobile-field-not-selected', `${path}.behavior.mobileCard.${part}`, `Telefon kartında (${part}) '${field}' seçili değil. Seçili: ${paths.join(' · ')}`);
249
+ }
250
+ }
251
+ const grouping = definition.grouping;
252
+ if (grouping) {
253
+ if (kind && kind !== 'entity')
254
+ issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.grouping`, message: `${kind} kaynağında gruplama yok: sunucu sırası ve gün/hafta/ay kovası yalnız entity kaynağında.` });
255
+ if (!grouping.by.trim() || !paths.includes(grouping.by))
256
+ subset('connect-data-grouping-not-selected', `${path}.grouping.by`, `Gruplama alanı '${grouping.by}' seçili değil. Seçili: ${paths.join(' · ')}`);
257
+ if (grouping.dateBucket && !CONNECT_DATA_GROUPING_DATE_BUCKETS.includes(grouping.dateBucket))
258
+ shape('connect-data-enum-invalid', `${path}.grouping.dateBucket`, "grouping.dateBucket yalnız 'day', 'week' ya da 'month' (alan date/datetime olmalı).");
259
+ if (grouping.sectionOrder && !CONNECT_DATA_GROUPING_SECTION_ORDERS.includes(grouping.sectionOrder))
260
+ shape('connect-data-enum-invalid', `${path}.grouping.sectionOrder`, "grouping.sectionOrder yalnız 'asc', 'desc' ya da 'asis'.");
261
+ if (grouping.sectionOrder !== 'asis' && definition.page?.mode === 'cursor' && (definition.sort ?? []).length > 0) {
262
+ issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.sort`, message: 'Gruplama + cursor sayfalamada sıra gruplama alanı ve anahtardır: sort verilemez — sort\'u kaldırın ya da offset sayfalayın.' });
263
+ }
264
+ }
206
265
  for (const [role, fieldPath] of Object.entries(definition.mapping?.roles ?? {})) {
207
266
  if (fieldPath?.trim() && !paths.includes(fieldPath))
208
267
  subset('connect-data-role-not-selected', `${path}.mapping.roles.${role}`, `'${role}' rolü seçili olmayan '${fieldPath}' alanına eşlenmiş.`);
@@ -216,8 +275,20 @@ export function connectDataShapeIssues(ref, path = 'ref') {
216
275
  issues.push({ code: 'CD-011', rule: 'connect-data-lookup-nested', path: lookupPath, message: `'${owner}' lookup'ı içinde başka lookup olamaz.` });
217
276
  if (lookup.mode && !['drawer', 'modal'].includes(lookup.mode))
218
277
  shape('connect-data-enum-invalid', `${lookupPath}.mode`, "lookup.mode yalnız 'drawer' ya da 'modal'.");
278
+ if (lookup.mobileMode && !CONNECT_DATA_LOOKUP_MOBILE_MODES.includes(lookup.mobileMode))
279
+ shape('connect-data-enum-invalid', `${lookupPath}.mobileMode`, "lookup.mobileMode yalnız 'sheet' (alt sayfa, varsayılan) ya da 'fullscreen' (tam ekran).");
219
280
  if (lookup.page && (lookup.page.limit < 1 || lookup.page.limit > CONNECT_DATA_PAGE_LIMIT_MAX))
220
281
  shape('connect-data-page-limit', `${lookupPath}.page.limit`, `lookup.page.limit 1…${CONNECT_DATA_PAGE_LIMIT_MAX} arasında olmalı.`);
282
+ if (lookup.mobileRow) {
283
+ const lookupPaths = selectedPaths({ ...definition, fields: lookup.fields ?? [] });
284
+ const rowParts = [['title', lookup.mobileRow.title ?? ''], ['subtitle', lookup.mobileRow.subtitle], ['trailing', lookup.mobileRow.trailing]];
285
+ for (const [part, field] of rowParts) {
286
+ const isBlankTitle = part === 'title' && !field?.trim();
287
+ const isOutside = typeof field === 'string' && lookupPaths.length > 0 && !lookupPaths.includes(field);
288
+ if (isBlankTitle || isOutside)
289
+ subset('connect-data-mobile-field-not-selected', `${lookupPath}.mobileRow.${part}`, `Telefon satırında (${part}) '${field ?? ''}' '${owner}' lookup'ının alanı değil${isBlankTitle ? ' (başlık zorunlu)' : ''}. Lookup alanları: ${lookupPaths.join(' · ') || '(türetilir)'}`);
290
+ }
291
+ }
221
292
  }
222
293
  return issues;
223
294
  }
@@ -124,5 +124,19 @@ export const connectDataRuleCatalog = [
124
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
125
  enforcedBy: 'connect_data_bind — buildDefinition (dryRun dahil, istek gitmeden)',
126
126
  source: "38/M-4; ConnectDataService.ValidateSourcesAsync ConnectDataRules.EditableFile 'connect-data-editable-file' (422, kayıtta)"
127
+ },
128
+ {
129
+ id: 'CD-019',
130
+ severity: 'block',
131
+ rule: "Satırları bölümleyen bileşen (connectData.props.<prop>.wizardSteps 'grouping' — mobilde grouped-list) tanımda grouping ister: by seçili ve (asis dışında) sıralanabilir bir alan, sectionOrder asc · desc · asis; dateBucket (day · week · month) yalnız date/datetime alanda (describe dateBuckets).",
132
+ enforcedBy: 'connect_data_bind — buildDefinition (istek gitmeden); şekil ve alt küme connectDataShapeIssues (connect-data-grouping-not-selected, connect-data-enum-invalid)',
133
+ source: "kök ADR 0019 (plan mobil-tasarimci-kabuk-ekranlari K8, b-32); ConnectDataDefinitionValidator connect-data-grouping-not-selected, ConnectDataService 422 grouping.dateBucket ve ValidateSourcesAsync connect-data-grouping-bucket-not-date ve connect-data-grouping-not-sortable"
134
+ },
135
+ {
136
+ id: 'CD-020',
137
+ severity: 'block',
138
+ rule: "grouping yalnız satırları bölümleyen bileşende yazılır; gruplama adımı bildirmeyen bileşen (datatable, repeater, seçim bileşenleri…) onu okumaz.",
139
+ enforcedBy: 'connect_data_bind — buildDefinition (connectable.wizardSteps)',
140
+ source: 'kök ADR 0019; Studio Veri bağla sihirbazı gruplama adımını yalnız bildiren bileşende çizer (wizard-step-registry)'
127
141
  }
128
142
  ];