@appaflytech/zeyrek-ai-mcp 0.0.31 → 0.0.33

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 (91) hide show
  1. package/README.md +5 -5
  2. package/dist/config.js +0 -26
  3. package/dist/knowledge/backendConstraints.js +1 -18
  4. package/dist/knowledge/componentCatalog.js +27 -493
  5. package/dist/knowledge/componentEvents.js +2 -120
  6. package/dist/knowledge/connectDataDefinition.js +4 -4
  7. package/dist/knowledge/connectDataRules.js +1 -1
  8. package/dist/knowledge/mobileComponentCatalog.js +13 -8
  9. package/dist/knowledge/mobileStore.js +227 -0
  10. package/dist/knowledge/normalize.js +48 -131
  11. package/dist/knowledge/outlines.js +0 -36
  12. package/dist/knowledge/platform/lint.js +1 -1
  13. package/dist/knowledge/platform/nativeBoxModel.js +1 -1
  14. package/dist/knowledge/policy.js +4 -18
  15. package/dist/knowledge/process.js +1 -1
  16. package/dist/knowledge/reservedTables.js +0 -79
  17. package/dist/knowledge/rules.js +10 -83
  18. package/dist/knowledge/schemaGuard.js +0 -11
  19. package/dist/observability/trace.js +2 -5
  20. package/dist/platform/capture/capture.js +1 -25
  21. package/dist/platform/errors.js +10 -162
  22. package/dist/platform/http.js +1 -6
  23. package/dist/platform/responses.js +4 -34
  24. package/dist/platform/schemaReadMemory.js +2 -2
  25. package/dist/platform/secrets.js +1 -8
  26. package/dist/resources/examples/middleware-authenticate.json +1 -1
  27. package/dist/resources/examples/mobile-app-shell-drawer.json +62 -61
  28. package/dist/resources/examples/mobile-list-detail-form.json +51 -20
  29. package/dist/resources/examples/page-detail-profile.json +1 -2
  30. package/dist/resources/examples/page-dynamic-form.json +2 -3
  31. package/dist/resources/examples/page-html-embed.json +1 -1
  32. package/dist/resources/examples/workflow-basic.json +2 -8
  33. package/dist/resources/guides/access.md +4 -18
  34. package/dist/resources/guides/bpm.md +1 -6
  35. package/dist/resources/guides/component.md +4 -83
  36. package/dist/resources/guides/entity.md +8 -50
  37. package/dist/resources/guides/getting-started.md +1 -38
  38. package/dist/resources/guides/merge.md +3 -37
  39. package/dist/resources/guides/microservice.md +0 -78
  40. package/dist/resources/guides/middleware.md +3 -35
  41. package/dist/resources/guides/mobile.md +85 -2
  42. package/dist/resources/guides/page.md +26 -278
  43. package/dist/resources/guides/workflow.md +0 -51
  44. package/dist/resources/rules.js +5 -11
  45. package/dist/resources/schema-fingerprint.json +5 -7
  46. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  47. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  48. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  49. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -146
  50. package/dist/resources/schemas/DataJobDto.json +1 -6
  51. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +2 -20
  52. package/dist/resources/schemas/MicroserviceDto.json +1 -13
  53. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  54. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -25
  55. package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
  56. package/dist/resources/schemas/StepDto.json +1 -25
  57. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  58. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  59. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  60. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  61. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -146
  62. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +259 -0
  63. package/dist/server.js +5 -25
  64. package/dist/tools/access.js +2 -369
  65. package/dist/tools/common.js +0 -7
  66. package/dist/tools/components.js +2 -15
  67. package/dist/tools/datasources.js +2 -2
  68. package/dist/tools/dependencies.js +1 -1
  69. package/dist/tools/deployments.js +3 -38
  70. package/dist/tools/designSchemaPatch.js +3 -13
  71. package/dist/tools/dtos.js +5 -36
  72. package/dist/tools/entities.js +12 -79
  73. package/dist/tools/externalComponents.js +1 -1
  74. package/dist/tools/flows.js +10 -45
  75. package/dist/tools/folders.js +1 -1
  76. package/dist/tools/imports.js +2 -5
  77. package/dist/tools/layouts.js +5 -6
  78. package/dist/tools/merges.js +2 -2
  79. package/dist/tools/microservices.js +20 -50
  80. package/dist/tools/mobileStore.js +268 -0
  81. package/dist/tools/pages.js +35 -89
  82. package/dist/tools/platformInfo.js +6 -20
  83. package/dist/tools/projects.js +9 -71
  84. package/dist/tools/rows.js +12 -62
  85. package/dist/tools/schemaOps.js +1 -29
  86. package/dist/tools/screenWrite.js +1 -22
  87. package/dist/tools/screens.js +3 -6
  88. package/dist/tools/templates.js +1 -26
  89. package/dist/tools/translations.js +1 -1
  90. package/dist/tools/workflows.js +5 -10
  91. package/package.json +2 -2
@@ -4,9 +4,8 @@ import { reservedTenantTables } from './reservedTables.js';
4
4
  import { headersAccessNames, isHeadersAccessGuarded } from './flowGuards.js';
5
5
  import { callsItself, functionCallRefs, isValidParameterName, mentionsAwait, nameProblem, sourceOf, callPrefix } from './functionGuards.js';
6
6
  import { backendPolicyRules } from './backendConstraints.js';
7
- import { accessPolicyRules } from './access.js';
8
7
  import { themePolicyRules } from './themeRules.js';
9
- import { processPolicyRules } from './process.js';
8
+ import { mobileStorePolicyRules } from './mobileStore.js';
10
9
  const terminalStepTypes = new Set(['response', 'convert', 'revert']);
11
10
  const DESCRIPTION_EXEMPT_STEPS = new Set(['start']);
12
11
  const stepKindMatrix = {
@@ -21,7 +20,6 @@ const stepKindMatrix = {
21
20
  email: ['workflow', 'automation', 'middleware'],
22
21
  'file-upload': ['workflow', 'automation', 'middleware'],
23
22
  microfunction: ['workflow', 'automation', 'middleware'],
24
- process: ['workflow', 'automation'],
25
23
  variable: ['middleware'],
26
24
  'set-user': ['middleware'],
27
25
  setcookie: ['middleware'],
@@ -164,7 +162,6 @@ const flowBranchNames = {
164
162
  'notification-permission': ['always'],
165
163
  'notification-schedule': ['always', 'error'],
166
164
  'notification-cancel': ['always'],
167
- bpm: ['always', 'success', 'fail'],
168
165
  end: []
169
166
  };
170
167
  /**
@@ -176,7 +173,7 @@ const flowBranchNames = {
176
173
  export const knownFlowStepTypes = new Set([
177
174
  'start', 'end', 'query', 'setter', 'download', 'condition', 'redirect', 'setcookie', 'alert', 'confirm',
178
175
  'microservice', 'validation', 'flow-trigger', 'storage', 'navigate', 'code', 'function', 'share',
179
- 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel', 'bpm'
176
+ 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel'
180
177
  ]);
181
178
  /**
182
179
  * Cihaz yeteneği adımlarının kapalı kümeleri — `@ng-apps/flow` `tasks/device/device.functions.ts`
@@ -356,7 +353,7 @@ function unknownFilterOperators(filters) {
356
353
  const operator = String(filter.operator);
357
354
  const lower = operator.toLowerCase();
358
355
  const suggestion = filterOperators.has(lower) ? lower : filterOperatorSuggestions[lower];
359
- return `${String(filter.field ?? filter.fieldSlug ?? filter.fieldId ?? '?')}: '${operator}'${suggestion ? ` (${suggestion} mı demek istedin?)` : ''}`;
356
+ return `${String(filter.fieldSlug ?? filter.fieldId ?? '?')}: '${operator}'${suggestion ? ` (${suggestion} mı demek istedin?)` : ''}`;
360
357
  });
361
358
  }
362
359
  const unknownOperatorConsequence = "Bilinmeyen işleç yola ve backend sürümüne göre ya 400 alır ya sessizce 'eq'e düşer ve sorgu yanlış sonuç döner (2026-09-27'den sonraki backend uç çağrısının `filters` parametresini 400 ile reddeder; workflow tablo adımı hâlâ sessizce 'eq' yapar); ad büyük/küçük harfe duyarlıdır — doğru işleci yazın";
@@ -500,27 +497,6 @@ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Arra
500
497
  const functionsGuide = 'ngapps://guides/functions';
501
498
  /** FN-006: bu kadar dolu satırdan uzun gövde yorumsuz kalmaz; tek satırlık `return` yorum istemez. */
502
499
  const FUNCTION_COMMENT_MIN_LINES = 5;
503
- /**
504
- * BPM-022: `process.*` sistem işlemi karşılığı olan `Page.bpm` üyeleri (backend
505
- * `SystemOperationConstants`, zeyrek-backend/docs/bpm/sablon-paketleri.md §Sistem işlemleri).
506
- * `start`, `cases`, `documents`, `analytics`, `exportLog`, `offline`, `legalHold(s)` karşılıksızdır;
507
- * onları kullanan akışa uyarı vermek yol gösteremeyen gürültüdür.
508
- */
509
- const PAGE_BPM_SYSTEM_MEMBERS = new Set([
510
- 'definitions', 'users', 'directory', 'tasks', 'task', 'instances', 'instance', 'timeline', 'signal',
511
- 'batch', 'batchOperation', 'delegations', 'delegate', 'delegation', 'handover', 'notifications',
512
- 'notification', 'preferences', 'override', 'report'
513
- ]);
514
- /**
515
- * BPM-022: karşılığı olan üyenin sistem işlemi OLMAYAN alt çağrıları (bpm.runtime.ts). Bunlar ve
516
- * `batch('start', …)` eski yolda kalır; taze bağlam incelemesi (2026-09-23) üye düzeyindeki bakışın
517
- * `instance(id).exportLedger()` gibi rehberin kendi önerdiği çağrıları uyardığını buldu.
518
- */
519
- const PAGE_BPM_UNMAPPED_CALLS = new Set([
520
- 'tasks.calendar', 'task.comment', 'task.workedMinutes', 'task.assistant', 'instance.exportLedger',
521
- 'instance.watch', 'instance.unwatch', 'instance.comments', 'handover.history', 'override.conflicts',
522
- 'batchOperation.get'
523
- ]);
524
500
  export const policyRules = [
525
501
  {
526
502
  id: 'MS-001',
@@ -800,7 +776,7 @@ export const policyRules = [
800
776
  return [{
801
777
  ruleId: 'WF-005',
802
778
  severity: 'block',
803
- message: `'${stepLabel(step)}' step'i (${step.type}) ${kind} tipinde kullanılamaz. İzin matrisi: variable/set-user/setcookie/redirect yalnız middleware; convert yalnız workflow; revert yalnız automation; response workflow+middleware; process workflow+automation.`,
779
+ message: `'${stepLabel(step)}' step'i (${step.type}) ${kind} tipinde kullanılamaz. İzin matrisi: variable/set-user/setcookie/redirect yalnız middleware; convert yalnız workflow; revert yalnız automation; response workflow+middleware.`,
804
780
  guide: 'ngapps://guides/workflow'
805
781
  }];
806
782
  });
@@ -2527,7 +2503,7 @@ export const policyRules = [
2527
2503
  })
2528
2504
  },
2529
2505
  {
2530
- id: 'FLOW-035',
2506
+ id: 'FLOW-027',
2531
2507
  module: 'flow',
2532
2508
  appliesTo: ['create', 'update'],
2533
2509
  sourceRef: "share adımının argümanı StepManagementArgument'ın Title/Message/Path alanlarında saklanır (StepType.cs `share`, kök docs/adr/0001-share-akis-adimi.md); url/link/text gibi anahtarlar sözleşmede YOKTUR ve platform onları 200 ile sessizce düşürür — adım hiçbir şey paylaşmadan akış sürer. `message` ve `path` İFADEDİR (@ng-apps/flow resolveShareContent → runCode): tırnaksız 'https://…' JS'te etiket + yorumdur, undefined döner",
@@ -2541,7 +2517,7 @@ export const policyRules = [
2541
2517
  const strayTexts = ['text', 'body', 'content'].filter(filled);
2542
2518
  if (strayLinks.length > 0) {
2543
2519
  findings.push({
2544
- ruleId: 'FLOW-035',
2520
+ ruleId: 'FLOW-027',
2545
2521
  severity: filled('path') ? 'warn' : 'block',
2546
2522
  message: `'${flowStepLabel(step)}' share adımının bağlantısı args.${strayLinks.join('/args.')} alanında: bu anahtar StepManagementArgument sözleşmesinde YOK, platform onu 200 ile sessizce düşürür ve adım bağlantısız kalır. Bağlantıyı args.path'e ifade olarak yazın — ör. "'https://ornek.app/urun/' + Page.queryParams.id".`,
2547
2523
  guide: 'ngapps://guides/page'
@@ -2549,7 +2525,7 @@ export const policyRules = [
2549
2525
  }
2550
2526
  if (strayTexts.length > 0) {
2551
2527
  findings.push({
2552
- ruleId: 'FLOW-035',
2528
+ ruleId: 'FLOW-027',
2553
2529
  severity: filled('message') ? 'warn' : 'block',
2554
2530
  message: `'${flowStepLabel(step)}' share adımının metni args.${strayTexts.join('/args.')} alanında: bu anahtar StepManagementArgument sözleşmesinde YOK, platform onu sessizce düşürür. Metni args.message'a ifade olarak yazın — ör. "'Şuna bak: ' + Page.properties.name".`,
2555
2531
  guide: 'ngapps://guides/page'
@@ -2559,7 +2535,7 @@ export const policyRules = [
2559
2535
  const value = typeof args[key] === 'string' ? String(args[key]).trim() : '';
2560
2536
  if (/^[a-z][a-z\d+\-.]*:\/\//i.test(value)) {
2561
2537
  findings.push({
2562
- ruleId: 'FLOW-035',
2538
+ ruleId: 'FLOW-027',
2563
2539
  severity: 'block',
2564
2540
  message: `'${flowStepLabel(step)}' share adımının '${key}' alanı tırnaksız bir adres: '${value}'. Bu alan İFADEDİR — tırnaksız adres JS'te etiket + yorumdur, undefined döner ve hiçbir şey paylaşılmaz. Dize olarak yazın: "'${value}'".`,
2565
2541
  guide: 'ngapps://guides/page'
@@ -3000,29 +2976,6 @@ export const policyRules = [
3000
2976
  return findings;
3001
2977
  })
3002
2978
  },
3003
- {
3004
- id: 'BPM-022',
3005
- module: 'flow',
3006
- appliesTo: ['create', 'update'],
3007
- sourceRef: "bpm-management 1.1.0 sayfaları süreç çalışma zamanına yalnız source: system mikroservislerle gider (plans/bpm-sistem-mikroservis D-1 a, ADR-0004); Page.bpm aynı uçlara mikroservissiz gider, rehberi okuyan ajanlar yine de yazdı (plans/bpm/demo koşuları)",
3008
- check: definition => flowAllSteps(definition).flatMap((step) => {
3009
- const calls = [...new Set(flowExpressionSites(step)
3010
- .flatMap(site => [...site.expression.matchAll(/\bPage\s*\.\s*bpm\b(?:\s*\.\s*([A-Za-z_$][\w$]*))?(\s*\((?:[^()]|\([^()]*\))*\))?(?:\s*\.\s*([A-Za-z_$][\w$]*))?/g)])
3011
- .filter(([, member = '', callArguments = '', subCall]) => member === ''
3012
- || (PAGE_BPM_SYSTEM_MEMBERS.has(member)
3013
- && !(subCall && PAGE_BPM_UNMAPPED_CALLS.has(`${member}.${subCall}`))
3014
- && !(member === 'batch' && /^\s*\(\s*['"]start['"]/.test(callArguments))))
3015
- .map(([, member, , subCall]) => (member ? `Page.bpm.${member}${subCall ? `(…).${subCall}` : ''}` : 'Page.bpm')))];
3016
- return calls.length === 0
3017
- ? []
3018
- : [{
3019
- ruleId: 'BPM-022',
3020
- severity: 'warn',
3021
- message: `'${flowStepLabel(step)}' adımı Page.bpm çağırıyor (${calls.join(', ')}). Sayfa akışı süreç çalışma zamanıyla source: system mikroservis üzerinden konuşur: microservice_system_operation_list ile process.* işlemini seçin, microservice_create { source: 'system', systemOperation } ile ucu kurun ve çağrıyı bir microservice adımına taşıyın; sonucu okuyan code adımı Steps.<adım>.data / .error'dan okur. Page.bpm eski yoldur — izin ve görünürlük kararı sistem işleminde aynı servisten gelir, uç Studio'da ve microservice_execute ile görünür.`,
3022
- guide: 'ngapps://guides/bpm'
3023
- }];
3024
- })
3025
- },
3026
2979
  {
3027
2980
  id: 'MS-013',
3028
2981
  module: 'microservice',
@@ -3281,37 +3234,11 @@ export const policyRules = [
3281
3234
  }];
3282
3235
  }
3283
3236
  },
3284
- {
3285
- id: 'MW-007',
3286
- module: 'workflow',
3287
- appliesTo: ['create', 'update', 'preview'],
3288
- sourceRef: "Sayfa middleware'inin variable adımı WorkflowContext değişkenine yazar ve sayfa yanıtı bu değişkenleri Page.variables olarak istemciye taşır (GetPageBySlugQuery → PageAppDto.Map(workflowContext.ClientVariables), önbellek isabetinde ExecutePageBehavior). rule.isPrivate işaretli değişken sunucuda kalır (WorkflowContext.AddPrivateVariable). qm '[SY] Settings Setter' { ...settings.data } ile sso_client_secret'ı istemciye verdi (2026-10-02)",
3289
- check: (definition, context) => {
3290
- if (context.workflowKind !== 'middleware')
3291
- return [];
3292
- const exposing = workflowSteps(definition).filter(step => {
3293
- if (step.type !== 'variable' || step.rule?.isPrivate === true)
3294
- return false;
3295
- const expression = String(step.rule?.expression ?? '');
3296
- return /\.\.\.\s*[A-Za-z_$]/.test(expression) || /secret|password|passwd|api_?key|private_?key/i.test(expression);
3297
- });
3298
- return exposing.length === 0
3299
- ? []
3300
- : [{
3301
- ruleId: 'MW-007',
3302
- severity: 'warn',
3303
- message: `'${exposing.map(stepLabel).join(', ')}' değişkeni bütün bir kaydı yayıyor ya da gizli değer adı taşıyor. Bu middleware bir SAYFAYA bağlanırsa değişken Page.variables ile istemciye gider (tarayıcıda/cihazda okunur). Gizli değer taşıyan değişkene rule.isPrivate: true verin — aynı istekteki sonraki middleware'ler okumaya devam eder, istemciye gitmez (sayfanın akışları istemcide koşar ve Page.variables'ı okur: işaretli değişken orada undefined'dır); istemcinin gerçekten ihtiyaç duyduğu alanları ayrı bir değişkende izin listesiyle kurun: '({ app_name: settings.data.app_name })' (platform_guide('middleware') Tarif 2). Yalnız microservice uçlarına bağlı middleware'de değişken istemciye dönmez.`,
3304
- guide: 'ngapps://guides/middleware'
3305
- }];
3306
- }
3307
- },
3308
3237
  // Backend doğrulayıcılarının aynası (ENT-015…018, MS-017, WF-026, QRY/VIEW/DTO/ROW/DS/PRJ/TR/FLD/FILE/EXT):
3309
3238
  // tek liste `backendConstraints.ts`'te durur, buradan yalnız politika motoruna katılır.
3310
3239
  ...themePolicyRules,
3311
- ...backendPolicyRules(),
3312
- // Organizasyon şeması, erişim politikası ve alan koruması (ORG/ACC/PROT) — `access.ts`.
3313
- ...accessPolicyRules(),
3314
- ...processPolicyRules()
3240
+ ...mobileStorePolicyRules,
3241
+ ...backendPolicyRules()
3315
3242
  ];
3316
3243
  /**
3317
3244
  * WF-MF-001'in ikinci yarısı. Bir adımın koşup koşmayacağı yalnız GÖVDEDEN anlaşılamaz: karar
@@ -49,13 +49,6 @@ function referenceName(node) {
49
49
  return referenceName(allOf[0]);
50
50
  return undefined;
51
51
  }
52
- /**
53
- * `System.Text.Json.Nodes.JsonNode` gibi serbest JSON değerlerinin üretilen şeması katıdır
54
- * (`options/parent/root`); içine inilirse meşru anahtarlar "platform yok sayıyor" diye raporlanır.
55
- */
56
- function isFreeFormJson(node) {
57
- return /(^|.)Json(Node|Object|Array|Value|Element)$/.test(referenceName(node) ?? '');
58
- }
59
52
  function resolve(document, node) {
60
53
  if (!node || typeof node !== 'object')
61
54
  return undefined;
@@ -83,8 +76,6 @@ function walk(document, schema, value, path, into) {
83
76
  if (!schema || !value || typeof value !== 'object')
84
77
  return;
85
78
  if (Array.isArray(value)) {
86
- if (isFreeFormJson(schema.items))
87
- return;
88
79
  const items = resolve(document, schema.items);
89
80
  value.forEach((entry, index) => walk(document, items, entry, `${path}[${index}]`, into));
90
81
  return;
@@ -104,8 +95,6 @@ function walk(document, schema, value, path, into) {
104
95
  into.push(child);
105
96
  continue;
106
97
  }
107
- if (isFreeFormJson(properties[key]))
108
- continue;
109
98
  walk(document, resolve(document, properties[key]), entry, child, into);
110
99
  }
111
100
  }
@@ -204,12 +204,9 @@ function describeResult(result) {
204
204
  const text = Array.isArray(call?.content)
205
205
  ? call.content.filter(entry => entry.type === 'text').map(entry => String(entry.text)).join(' ')
206
206
  : '';
207
+ if (call?.isError)
208
+ return { ok: false, summary: clip(text, 400) };
207
209
  const structured = call?.structuredContent;
208
- if (call?.isError) {
209
- return structured?.expectedFailure === true
210
- ? { ok: true, summary: `beklenen platform hatası (negatif test): ${clip(text, 400)}` }
211
- : { ok: false, summary: clip(text, 400) };
212
- }
213
210
  const runtimeErrors = structured?.runtimeErrors;
214
211
  if (Array.isArray(runtimeErrors) && runtimeErrors.length > 0) {
215
212
  // Kasıtlı negatif test bir kusur değil bir KANIT: çağıran beklediğini söylediyse satır
@@ -6,22 +6,6 @@ import { actionScript, activeTabScript, rootElementId, runtimePropsScript } from
6
6
  import { domSummaryScript } from './summary.js';
7
7
  import { createPageConsoleLog, describePageFailure, isExpressionEcho, parseRenderErrors, renderErrorsScript, splitExpressionErrors } from './console-log.js';
8
8
  import { decideReadiness, describeUnready, parseReadiness, readinessScript, runtimePath } from './readiness.js';
9
- /** Runtime'ın Connect Data bağlantısı için bastığı sözde akış kimliği: hata bir akış adımından değil bağlı prop'un okumasından gelir. */
10
- const CONNECT_DATA_FLOW_ID = 'connect-data';
11
- /**
12
- * `runtimeErrors` yanında dönen okuma notu. Bağlantı hatasının (`flowId: 'connect-data'`) açılacak akışı yoktur:
13
- * not onu bağlantının araçlarına yönlendirir (39/M-18 — `flow_get` önerisi ajanı olmayan bir akışa götürüyordu).
14
- */
15
- export function runtimeErrorsNote(errors) {
16
- const connections = errors.filter(error => error.flowId === CONNECT_DATA_FLOW_ID);
17
- const flows = errors.length - connections.length;
18
- const head = `Sayfa ${errors.length} yapısal hata bıraktı; kare 'çalışıyor' görünse bile bu adımlar işini YAPMADI. element/property alanı hangi komponentin hangi prop'unun yazılamadığını söyler — o komponent eski/boş değerindedir.`;
19
- const flowHint = flows > 0 ? ` Akış hatasında (${flows}) adımı flow_get ile açıp ifadeyi düzeltin.` : '';
20
- const connectionHint = connections.length > 0
21
- ? ` Bağlantı hatasında (flowId 'connect-data': ${connections.map(error => `${error.element}.${error.property}`).join(', ')}) akış yoktur: code alanı reddin nedenidir — connect_data_get ile tanımı, connect_data_execute { op: 'list', asUserId } ile aynı isteği görün (kompleks projede 403 çoğunlukla kaynağın grant'i ya da ACC-005).`
22
- : '';
23
- return `${head}${flowHint}${connectionHint}`;
24
- }
25
9
  /**
26
10
  * Ziyaretçinin tema tercihinin yaşadığı localStorage anahtarı — `theme-toggle` bunu yazar,
27
11
  * `Theme` okur ve projenin kendi modunun önüne geçirir
@@ -488,7 +472,7 @@ export class StudioScreenshotter {
488
472
  });
489
473
  context = await browser.newContext(contextOptions);
490
474
  }
491
- logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}${request.colorScheme ? ` · tema ${request.colorScheme}` : ''}${request.motion === 'play' ? ' · hareket oynar' : ''}${request.headers ? ` · başlık: ${Object.keys(request.headers).join(', ')}` : ''}`);
475
+ logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}${request.colorScheme ? ` · tema ${request.colorScheme}` : ''}${request.motion === 'play' ? ' · hareket oynar' : ''}`);
492
476
  try {
493
477
  const cookies = new Map();
494
478
  if (request.token)
@@ -498,14 +482,6 @@ export class StudioScreenshotter {
498
482
  if (cookies.size > 0) {
499
483
  await context.addCookies([...cookies].map(([name, value]) => ({ name, value, url: request.cookieUrl, httpOnly: false })));
500
484
  }
501
- // Kullanıcı gözü başlığı sayfa açılmadan kurulur: ilk veri isteği de o kullanıcının gözüyle gitmeli. Yeteneği
502
- // olmayan bağlamda kare SESSİZCE gateway kimliğiyle çekilmez, hata verir.
503
- if (request.headers && Object.keys(request.headers).length > 0) {
504
- if (!context.setExtraHTTPHeaders) {
505
- throw new PlatformApiError(`Başlıklar (${Object.keys(request.headers).join(', ')}) uygulanamadı: tarayıcı bağlamı setExtraHTTPHeaders desteklemiyor. Kare gateway kimliğiyle çekilir ve bu sessizce yanlış olurdu.`);
506
- }
507
- await context.setExtraHTTPHeaders(request.headers);
508
- }
509
485
  // Tema tercihi çerezde değil localStorage'da yaşar ve tema store'u değerini modül İLK
510
486
  // değerlendirildiğinde okur: sayfa açıldıktan sonra yazmak bir sonraki yüklemeye kadar
511
487
  // etkisizdir. Bu yüzden tercih, sayfanın script'lerinden önce koşan bağlam script'iyle
@@ -7,11 +7,6 @@ export class PlatformApiError extends Error {
7
7
  * için sayıya ihtiyaç duyar, çağıran hangi satırı düzelteceğini ancak öyle bilir.
8
8
  */
9
9
  rowIndex;
10
- /**
11
- * Platformun ham `message`'ı (yalnız 403'te doldurulur). `message` gateway ipucunu da taşır ve ipucunda
12
- * izin slug'ları geçer ('entity.reveal grant'i'); `accessDecision` izni ondan okursa yanlış izin yazar.
13
- */
14
- apiMessage;
15
10
  constructor(message, status, code) {
16
11
  super(message);
17
12
  this.status = status;
@@ -37,12 +32,9 @@ function collectValidationMessages(payload) {
37
32
  continue;
38
33
  const record = entry;
39
34
  const isPageSchemaShape = 'rule' in record || 'componentId' in record || 'suggestions' in record;
40
- const isDefinitionFindingShape = 'path' in record && 'message' in record && 'code' in record;
41
35
  messages.push(isPageSchemaShape
42
36
  ? formatPageSchemaError(record)
43
- : isDefinitionFindingShape
44
- ? `${String(record.path)} [${String(record.code)}]: ${String(record.message)}`
45
- : `${record.key ?? '?'}: ${record.value ?? ''}`);
37
+ : `${record.key ?? '?'}: ${record.value ?? ''}`);
46
38
  }
47
39
  }
48
40
  else if (payload.errors && typeof payload.errors === 'object') {
@@ -114,39 +106,6 @@ function importHint(code, path) {
114
106
  }
115
107
  return '';
116
108
  }
117
- function processHint(code, path) {
118
- if (code === 'DefinitionInvalid' && /\/bpm\/definitions\/[^/]+\/override(\/|$)/.test(path))
119
- return " Uyarlama platform doğrulamasından geçmedi; yukarıdaki path [kod] satırlarına bakın. RegionLocked/ParamNotEditable: yol uyarlamaya kapalı — process_override_get yanıtındaki editableScopes'a göre kurun (bölge çocuğu '/nodes[id=<bölge>]/nodes[id=<adım>]/<alan>'); RoleUnknown/WorkflowUnknown: referans bu ortamda yok. Kurallar platform_guide('bpm') §Uyarlama.";
120
- if (code === 'TaskAlreadyClaimed')
121
- return ' Görev başkası tarafından üstlenildi ya da siz okuduktan sonra değişti. process_task_get ile yeniden okuyup güncel version değeriyle tekrar deneyin; görev başkasındaysa eylem sizin değildir.';
122
- if (code === 'TaskStateTransitionNotAllowed')
123
- return " Görevin durumu bu eylemi almıyor (ör. waiting-info iken approve, tamamlanmış görevde claim). process_task_get'teki state ve actions listesine bakın.";
124
- if (code === 'TaskCommentRequired')
125
- return " Bu karar yorum ister (düğümün commentRequired kuralı). Aynı çağrıyı 'comment' ile tekrarlayın.";
126
- if (code === 'UndoWindowClosed')
127
- return " Geri alma penceresi kapandı ya da karardan sonra başka bir insan görevi açıldı; geri alma yerine process_instance_update ya da yeni karar gerekir.";
128
- if (code === 'InstanceStateTransitionNotAllowed')
129
- return " Örnek bu durumda bu müdahaleyi almaz (ör. tamamlanmış örneği askıya alma). process_instance_get ile state'i okuyun.";
130
- if (code === 'WithdrawNotAllowed')
131
- return " Tanımın withdrawPolicy'si geri çekmeye izin vermiyor ya da ilk karar verilmiş.";
132
- if (code === 'DefinitionInvalid')
133
- return " Tanım platform doğrulamasından geçmedi; yukarıdaki path [kod] satırlarını düzeltip process_definition_preview ile yeniden doğrulayın.";
134
- if (code === 'OpenTasksExist')
135
- return " Kullanıcının ya da birimin açık süreç görevi var. process_handover_preview ile kalemleri görün, process_handover_create ile devredin; sonra işlemi tekrarlayın.";
136
- if (code === 'HandoverIncomplete')
137
- return " Kararı olmayan görev grupları mesajda. Her assigned-tasks / candidate-tasks kalemi için karar verin ya da force:true ile geri dönüşü kabul edin: assigned-tasks yöneticiye (yoksa admin-queue), candidate-tasks gruba (aday kalmazsa admin-queue).";
138
- if (code === 'DelegationOverlap')
139
- return " Aynı kişinin bu aralık ve süreç filtresiyle çakışan vekaleti var. process_delegation_list ile görüp bitirin ya da aralığı değiştirin.";
140
- if (code === 'DelegationChainNotAllowed')
141
- return " Vekalet zincirlenemez: ya vekil aynı aralıkta kendi görevlerini devretmiş ya da vekaleti veren kişi o aralıkta başkasının vekili. Başka vekil ya da aralık seçin.";
142
- if (code === 'PublishApprovalRequired')
143
- return " Tanım dört göz yayın politikasında (publishPolicy: four-eyes) ve ortamda process-publish süreci kurulu. Aynı çağrıyı process_definition_publish { reason } ile tekrarlayın: yayın isteği açılır, onaylanınca sürüm aktif olur.";
144
- if (code === 'ScenarioFailed')
145
- return " Tanım requireGreenScenarios: true ve adı geçen senaryo bu taslağın sağlamasında yeşil koşmadı. process_scenario_execute { definitionId } ile koşun, sapmaları giderin (ya da senaryoyu process_scenario_update ile güncelleyin), sonra yeniden yayınlayın.";
146
- if (code === 'BatchOperationNotUndoable')
147
- return " Yalnız tamamlanmış reassign / extend-due işlemleri bir kez geri alınır; start ve cancel geri alınamaz.";
148
- return '';
149
- }
150
109
  function pageSchemaHint(code, path) {
151
110
  if (code === 'PageSchemaVersionConflictException') {
152
111
  return ' Sayfa siz okuduktan sonra değişti ve HİÇBİR op uygulanmadı. page_schema_get ile yeniden okuyup planı tazeleyin, yeni `version` değerini `baseVersion` olarak gönderin.';
@@ -183,20 +142,12 @@ function bulkRowHint(code) {
183
142
  function versionConflictHint(code, path) {
184
143
  if (code !== 'AggregateVersionConflictException')
185
144
  return '';
186
- if (/\/bpm\/tasks(\/|$)/.test(path)) {
187
- return ' Görev siz okuduktan sonra değişti (başka biri üstlendi ya da karar verdi) ve eylem UYGULANMADI. process_task_get ile yeniden okuyun; görev hâlâ size açıksa güncel `version` değeriyle eylemi tekrarlayın, değilse eylem sizin değildir.';
188
- }
189
- if (/\/bpm\/definitions\/[^/]+\/override(\/|$)/.test(path)) {
190
- return ' Uyarlama ya da aktif sürüm siz okuduktan sonra değişti (başka bir yönetici kaydetti ya da geliştirici yeni sürüm yayınladı) ve HİÇBİR op yazılmadı. process_override_get ile yeniden okuyun; conflictState conflicted ise patch yeni sürüme göre kurulmalı. process_override_update aynı çağrıda güncel sürümü okur — patch güncel uyarlamaya uyuyorsa çağrıyı tekrarlayın.';
191
- }
192
- const readTool = /\/bpm\/definitions(\/|$)/.test(path) ? 'process_definition_get'
193
- : /\/bpm\/instances(\/|$)/.test(path) ? 'process_instance_get'
194
- : /\/entities(\/|$)/.test(path) ? 'entity_get'
195
- : /\/workflows(\/|$)/.test(path) ? 'workflow_get'
196
- : /\/flows(\/|$)/.test(path) ? 'flow_get'
197
- : /\/layouts(\/|$)/.test(path) ? 'layout_get'
198
- : /\/microservices(\/|$)/.test(path) ? 'microservice_get'
199
- : 'ilgili *_get';
145
+ const readTool = /\/entities(\/|$)/.test(path) ? 'entity_get'
146
+ : /\/workflows(\/|$)/.test(path) ? 'workflow_get'
147
+ : /\/flows(\/|$)/.test(path) ? 'flow_get'
148
+ : /\/layouts(\/|$)/.test(path) ? 'layout_get'
149
+ : /\/microservices(\/|$)/.test(path) ? 'microservice_get'
150
+ : 'ilgili *_get';
200
151
  return ` Kayıt siz okuduktan sonra değişti ve HİÇBİR op uygulanmadı (mesajdaki beklenen/mevcut sürüm). ${readTool} ile yeniden okuyup patch'i güncel hâle göre kurun, yeni \`version\` değerini \`baseVersion\` olarak gönderin — aynı ops'u eski sürümle tekrar göndermek aradaki değişikliği ezer.`;
201
152
  }
202
153
  /**
@@ -226,69 +177,6 @@ function diagnoseKnownFailure(status, message, code, context) {
226
177
  }
227
178
  return '';
228
179
  }
229
- /**
230
- * Erişim politikası ve alan koruması hataları `InvalidParameterException` kodu altında gelir ve
231
- * ayırt edici anahtar MESAJDADIR (`AccessScopeNotSupported: entity.create`); koşul ve koruma
232
- * hatalarının bir kısmı ise kendi exception adını `code`'da taşır. İkisi de burada tek sözlükle
233
- * bir sonraki adıma çevrilir (rol yönetim şeması g-21).
234
- */
235
- const accessErrorHints = [
236
- ['ConnectDataDefinitionInvalid', " Bağlantı tanımı platform şemasına uymuyor (bilinmeyen anahtar, bozuk enum ya da eksik alan) — mesajdaki yolu düzeltin; kural adları connect-data-* (PAGE-035 · CD-004). Tanım şekli: platform_guide('page') §Kural 1c-3."],
237
- ['ConnectDataPropNotConnected', " Bu prop veri kaynağına bağlı değil — önce connect_data_bind (ya da page_schema_patch set_connect_data) ile bağlayın; bir prop'un bağlantısını connect_data_get okur."],
238
- ['ConnectDataFilterNotAllowed', " Filtre, arama ya da sıralama bu bağlantıda izinli değil: alan tanımın behavior.filterable/searchable listesinde olmalı ve işleç connect_data_describe'daki operators içinde; korumalı alanda yalnız eq · neq · in · notin, korumalı alan sıralanmaz; tanım filtresindeki $parametre params ile verilmeli (CD-002)."],
239
- ['ConnectDataFieldNotEditable', " Alan tanımda seçili ama bağlantı ucundan yazılamaz: edit.editable listesinde değil ya da dosya alanı (dosya bu uçtan hiç yazılmaz). Yazılacaksa connect_data_bind { edit: { editable: [..., '<alan>'] } }; dosyayı file_upload ile yükleyip satırı row_update ile güncelleyin."],
240
- ['ConnectDataFieldNotSelected', " İstenen alan tanımın seçtiği alanlardan değil (fields/expand), lookup ilişki alanında değil ya da yazılan alan edit.editable listesinde değil (expand yolu hiç yazılamaz) — connect_data_get ile tanıma bakın; gerekirse connect_data_bind ile alanı ekleyin ya da edit.editable'a koyun."],
241
- ['ConnectDataWriteDisabled', " Tanımda bu yazma eylemi kapalı (behavior.edit) ya da edit.bulk kapalıyken birden çok satır/kimlik gönderildi — politika izin verse bile motor reddeder. Açmak için connect_data_bind { edit: { <op>: true } } (toplu için bulk: true)."],
242
- ['ConnectDataWriteModeNotAllowed', " Tanımın edit.writeMode'u atomic: satırların hepsi ya da hiçbiri yazılır. Kısmi yazma (satır başına errors[]) için connect_data_bind { edit: { writeMode: 'partial' } }, ya da isteği mode göndermeden atın."],
243
- ['yet: partial.', " Kısmi yazma (mode partial) yalnız built-in veritabanındaki entity'de çalışır — savepoint harici datasource bağlantısını kapsamaz. Bu kaynakta atomic yazın (mode göndermeyin, tanımda writeMode: 'atomic')."],
244
- ['yet: create.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
245
- ['yet: update.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
246
- ['yet: delete.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
247
- ['yet: lookup.', " Lookup yalnız entity kaynağında çalışır: ilişki hücresinin hedef tablosu entity'dir. Query/view/workflow/microservice kaynağında ilişki seçimi yoktur."],
248
- ['yet: datasource.', " Harici datasource (REST) kaynağı motorda YOK (b-33 kapsam dışı): platformda kayıtlı yürütücüsü yoktur. entity · query · view · workflow · microservice türlerinden birini seçin."],
249
- ['ConnectDataSourceUnsupported', " Kaynak bağlanabilir değil. Başlıca nedenler: uca middleware bağlı, yazan tablo/sistem ucu, dönüş şeması yok (workflow'a dizi dönen çıktı DTO'su, query'ye çalıştırılmış örnek gerekir), sorgu salt-okunur tek SELECT değil ya da kaynak zaten connectData microservice'i (tanımı doğrudan bağlayın, connect_data_get ile okuyun). Mesajın sonundaki değer bir op adıysa (create/update/delete/lookup) neden kaynağın bağlanabilirliği değil o işlemi desteklememesidir. connect_data_describe kaynağın ne yapabildiğini söyler."],
250
- ['ConnectDataMicroserviceInUse', " Bu connectData microservice'i başka bir prop'ta, başka bir sayfada ya da bir flow'da kullanılıyor: connect_data_detach tanımı geri koyup microservice'i silmek ister. Kullanımları dependency_graph ile bulup kaldırın, sonra yeniden deneyin."],
251
- ['ConnectDataRelationTargetNotFound', " İlişki alanına yazılan hedef kimlik hedef tabloda yok ya da bu kullanıcı onu göremiyor — yanıtın field'ı düşen alanı söyler. Var olan bir kimliği alanın lookup'ıyla seçin (connect_data_execute op: 'lookup', field: '<alan>'); m2m listesinde her kimlik ayrı denetlenir."],
252
- ['ConnectDataSourceFailed', " Kaynak (query/view/workflow/microservice) çalışırken düştü — hata kaynağın kendisinden gelir: microservice_execute / query_preview / workflow_preview ile kaynağı tek başına koşturup gerçek nedeni görün."],
253
- ['ProtectionOperatorNotAllowed', " Korumalı alanda yalnız eq · neq · in · notin · isnull · isnotnull kullanılır; şifreli modda eşitlik için alanın protection.exactSearch'ü açık olmalı (PROT-001). Kısmi arama için türetilmiş alan — platform_guide('access') §'Arama ve sıralama'."],
254
- ['ProtectionSortNotAllowed', " Korumalı alana göre sıralanamaz (PROT-002); başka bir alana ya da türetilmiş alana göre sıralayın."],
255
- ['ProtectionFieldTypeNotSupported', " Koruma yalnız düz METİN alana konur — ilişki, sayı, çok dilli, birincil anahtar ve identity alanı korunamaz (PROT-003)."],
256
- ['ProtectionDisplayFieldNotAllowed', " Display alanı korunamaz: ilişkilerde görünen ad olarak her yerde düz dönerdi (PROT-003). Önce başka bir alanı display yapın."],
257
- ['ProtectionDerived', " Türetilmiş alan derivedFromFieldId + derivationRule ister; slice length > 0, regex pattern derlenmeli (PROT-004)."],
258
- ['ProtectionChangeNotAllowed', " Var olan alanın korumasını entity_update / entity_patch değiştiremez; dolu satırlar arka plan işiyle dönüştürülür — entity_protection_update (202 + jobId, ilerleme import_status)."],
259
- ['ProtectionMigrationInProgress', " Bu entity'de koruma işi sürüyor; ilerlemeyi GET /{proje}/DataJobs/{id} ile izleyin. İş hata verdiyse AYNI isteği aynı gövdeyle tekrar göndermek kaldığı yerden devam ettirir."],
260
- ['ProtectionKeyUnavailable', " Ortamda alan koruması anahtarı yapılandırılmamış — insan işi: docs/ortam.md §Alan koruması anahtarları."],
261
- ['AccessScopeRequiresOwnerFields', " Entity'de sys_owner_user_id / sys_owner_unit_id yok (ACC-003): owner-fields işi (POST /{proje}/DataJobs/owner-fields/{datasourceId}) ya da scope: 'all' + koşul."],
262
- ['AccessResourceUnknown', " Anahtar kaynak kataloğunda yok — access_resource_list ile 'key' değerini alın (K-31); katalog entity/sayfa yazıldıkça kendiliğinden eşitlenir."],
263
- ['AccessConditionInvalid', " Koşul alanı grant'in entity'sinin korumasız, kataloğa girmiş bir alanı olmalı ve değer işlece uymalı (ACC-001/ACC-002) — alanlar: access_resource_list { type: 'field' }."],
264
- ['AccessScopeNotSupported', " Kapsam yalnız entity.read/update/delete/export/reveal/reassign izinlerinde (ACC-001)."],
265
- ['AccessFieldAccessNotSupported', " fieldAccess yalnız 'field.read' izninde (ACC-001)."],
266
- ['AccessDenyFieldAccessNotSupported', " Reddet grant'i alan erişimi taşımaz; kapsam ve koşul taşıyabilir, o zaman yalnız satır süzer (ACC-001) — koşulsuz scope: 'all' hariç, o kapsamsız gibi keser (ACC-042)."],
267
- ['AccessOverrideRequiresUserWithEndDate', " overridesDeny yalnız subjectType 'user' ve dolu endsAt ile yazılır (ACC-009)."],
268
- ['OrganizationPositionSeatOccupied', " Tek koltuklu pozisyonda aynı dönemde başka bir kalıcı atama var; vekâlet için assignmentType 'acting', kalıcı ikinci kişi için pozisyonu isMultiSeat yapın (TB-96, ORG-003)."],
269
- ['OrganizationStructureNodeInUse', " Düğüm DOLU. BİRİM için: altında aktif alt birim ya da aktif pozisyon var. POZİSYON için ÜÇ sebepten biri: koltukta oturan kullanıcı, o pozisyona bağlı (idari ya da işlevsel) başka bir aktif pozisyon, ya da pozisyonu managerPositionId olarak gösteren bir birim — mesaj hangisi olduğunu söylemez. Sıra yapraktan köke: user_position_replace ile koltukları boşalt → yöneticiyi gösteren birimi organization_unit_update ile managerPositionId'siz yaz → ast pozisyonları organization_position_deactivate → alt birimler → organization_unit_deactivate. Durumu organization_tree_get (includeInactive: true) ve user_position_list gösterir."],
270
- ['UserPositionPeriodInvalid', " Atamanın endsAt'i startsAt'ten sonra olmalı (ORG-003)."],
271
- ['UserPositionPrimaryDuplicate', " Bir kullanıcının en çok bir birincil ataması olur (ORG-003)."],
272
- ['RevealReasonTooShort', " Gerekçe projenin asgari uzunluğunun altında; project_get → revealReasonMinLength (TB-98, ACC-010). Reddedilen deneme denetim defterine REVEAL-DENIED olarak düşer."],
273
- ['EnforcementOffRequiresConfirmation', " off'a geçiş korumalı alanları herkese açık yapar ve satır kapsamını kaldırır; bilinçliyseniz confirm: true (TB-99, ACC-007)."],
274
- ['AccessConditionRequiresScope', " Koşul yalnız kapsamla birlikte yazılır; saf koşul için scope: 'all' (K-11, ACC-001)."],
275
- ['AccessPermissionUnknown', " İzin slug'ı katalogda yok — access_permission_list (katalog yalnız kompleks projede dolu)."],
276
- ['AccessPolicySlugDuplicate', ' Politika slug\'ı projede tekildir; access_policy_list ile mevcut politikayı bulun.'],
277
- ['EntityHasNoOwnerFields', " Entity'de sahiplik alanı (sys_owner_user_id / sys_owner_unit_id) yok, devredilecek bir sahiplik de yok: owner-fields işi (POST /{proje}/DataJobs/owner-fields/{datasourceId}) alanları açar. Basit projede bu alanlar hiç kurulmaz."],
278
- ['OwnershipFieldNotWritable', " Sahiplik alanı satır güncellemesiyle yazılmaz (ORG-006): row_reassign kullanın, ya da uygulama kullanıcısının kapsamıyla devretmek için systemOperation 'entity.row.reassign' olan bir uç + microservice_execute { asUser }."],
279
- ['UserCannotTransferToSelf', " Silinen kullanıcının kayıtları yine kendisine devredilemez: transferToUserId başka bir kullanıcı olmalı, ya da hiç verilmemeli (o zaman sahiplik birime düşer)."],
280
- ['OrganizationStructureParentInactive', " Üst düğüm pasifken alt düğüm etkinleştirilemez: kökten yaprağa ilerleyin — önce üst birimi organization_unit_activate ile, pozisyonda ise önce birimi, sonra idari/işlevsel üst pozisyonu organization_position_activate ile açın."],
281
- ['OrganizationSchemaRootCount', " Şema taslağında TEK kök birim olur: parentId'siz ikinci birim reddedilir. organization_schema_get ile kökü bulup yeni birimi onun (ya da bir altının) parentId'si ile açın."],
282
- ['OrganizationSchemaCycle', " Taslakta döngü var: bir birim/pozisyon kendi atasını ebeveyn gösteriyor. organization_schema_get ile zinciri izleyin."],
283
- ['OrganizationSchemaDuplicate', " Taslakta aynı kod (birim/pozisyon) ya da aynı rol slug'ı iki kez geçiyor; kodlar şema genelinde tekildir."],
284
- ['OrganizationSchemaReference', " Taslakta çözülemeyen referans var: pozisyonun unitId/roleId'si ya da birimin managerPositionId'si taslakta olmayan bir düğümü gösteriyor. Önce hedefi ekleyin, sonra gösteren düğümü yazın."],
285
- ['SystemEntityWriteNotAllowed', " Organizasyon ve erişim tabloları genel satır yoluyla yazılmaz (ORG-001) — platform_guide('access') §'Organizasyon ağacı'."]
286
- ];
287
- function accessHint(code, message) {
288
- const text = `${code ?? ''} ${message ?? ''}`;
289
- const found = accessErrorHints.find(([key]) => text.includes(key));
290
- return found ? found[1] : '';
291
- }
292
180
  const MaxErrorCauseWalk = 32;
293
181
  const MaxErrorPathShown = 6;
294
182
  const MaxErrorTextLength = 400;
@@ -374,45 +262,16 @@ export function mapPlatformError(status, payload, context) {
374
262
  if (validationMessages.length > 0) {
375
263
  return new PlatformApiError(`Doğrulama hatası (${location}): ${validationMessages.join(' ; ')}. Gövde şeması için ilgili ngapps://schemas kaynağına ya da mevcut bir kaydın *_get çıktısına bakın.`, status, code);
376
264
  }
377
- return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}${mergeRevertHint(code)}${importHint(code, context.path)}${accessHint(code, apiMessage)}`, status, code);
265
+ return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}${mergeRevertHint(code)}${importHint(code, context.path)}`, status, code);
378
266
  }
379
267
  if (status === 401) {
380
- // Uygulama oturumu 401'i gateway kimliğinin değil uygulama kullanıcısının sorunudur; USERNAME/PASSWORD
381
- // ipucu burada yanlış yere baktırırdı (g-31).
382
- if (code === 'AppSessionRequiredException' || code === 'AppSessionInvalidException') {
383
- return new PlatformApiError(`Uygulama oturumu ${code === 'AppSessionRequiredException' ? 'yok' : 'geçersiz ya da süresi dolmuş'} (${location}): ${apiMessage ?? ''} Kompleks proje + enforce ortamda oturumsuz her uç ve sayfa isteği 401 döner. Parolasız kullanıcı gözü için aynı çağrıyı asUserId: '<users satır id'si>' ile tekrarlayın (development/dal); oturum açılmış üye için access_session_create ile giriş yapıp tokenRefs'teki tok_… değerini asUser olarak verin.`, status, code);
384
- }
385
- // Yanlış e-posta/parola 2026-09-22'den beri kendi kodunu taşır (backend ADR 0004, K-3): oturum
386
- // hatası değildir, yenileme ya da gateway parolası ipucu yanlış yere baktırırdı.
387
- if (code === 'AppSignInFailedException') {
388
- return new PlatformApiError(`Uygulama girişi reddedildi (${location}): e-posta ya da parola yanlış, ya da kullanıcı bu projenin 'users' tablosunda yok. ${apiMessage ?? ''} Yeni kompleks projede users boş gelir — platform_guide('getting-started') §'Kompleks projede ilk kullanıcı'. Hız sınırı 5 dk'da 10 denemedir; tekrar denemeden önce kimlik bilgisini doğrulayın.`, status, code);
389
- }
390
- if (code === 'ReauthenticationRequiredException') {
391
- return new PlatformApiError(`İmza için yeniden kimlik doğrulaması reddedildi (${location}): password (verildiyse email de) görevi imzalayan kullanıcının gerçek parolası olmalı; gateway token'ı yenilemez, istek tekrarlanmadı (BPM-018).`, status, code);
392
- }
393
268
  const hint = context.authMode === 'local'
394
269
  ? 'Gateway konfigürasyonundaki USERNAME/PASSWORD doğru mu kontrol edin.'
395
270
  : 'İstemciden geçirilen Authorization header süresi dolmuş olabilir; yeni token alıp MCP kaydını güncelleyin.';
396
271
  return new PlatformApiError(`Kimlik doğrulanamadı (${location}). ${hint}`, status, code);
397
272
  }
398
273
  if (status === 403) {
399
- if (code === 'PublishApprovalRequired') {
400
- return new PlatformApiError(`Yayın dört göz onayı istiyor (${location}) [PublishApprovalRequired].${processHint(code, context.path)}`, status, code);
401
- }
402
- const runtimeHint = code === 'CsrfTokenMismatchException'
403
- ? " Oturum çerezle geldi ve durum değiştiren istekte X-ZY-CSRF yok: asUser kullanın (Bearer'lı istek CSRF istemez)."
404
- : code === 'AccessQueryNotScopedException'
405
- ? ' Sorgu kapsamlı/korumalı bir tabloya dokunuyor: kullanıcıya özel veri için table kaynağı, tüm veriyi özetleyen rapor için sorguda isUnrestricted (ACC-005).'
406
- : code === 'AccessFieldWriteDeniedException' || code === 'AccessFilterDeniedException' || code === 'AccessRevealDeniedException'
407
- ? ' Alan erişimi reddetti: access_preview fieldAccess (canWrite · canFilter); reveal için kapsamlı entity.reveal grant\'i.'
408
- : code === 'AccessOverridePermissionRequiredException'
409
- ? " Bu istek bir kullanıcı istisnasını yazıyor, genişletiyor ya da istisna taşıyan politikanın grant'lerine dokunuyor; access.override.manage izni gerekir (ACC-009)."
410
- : context.api === 'tenant' && code === 'ForbiddenException'
411
- ? ' Karar motoru reddetti; mesaj HANGİ iznin reddettiğini bilinçli olarak söylemez. Nedenini access_preview { userId, permission, resourceKey } ile sorun.'
412
- : '';
413
- const forbidden = new PlatformApiError(`Yetki yok (${location}). Token'ın sahibi bu işlem için yetkili değil.${runtimeHint}${runtimeHint ? '' : accessHint(code, apiMessage)}`, status, code);
414
- forbidden.apiMessage = apiMessage;
415
- return forbidden;
274
+ return new PlatformApiError(`Yetki yok (${location}). Token'ın sahibi bu işlem için yetkili değil.`, status, code);
416
275
  }
417
276
  if (status === 404) {
418
277
  const environmentHint = context.environment
@@ -421,17 +280,6 @@ export function mapPlatformError(status, payload, context) {
421
280
  if (context.method !== 'GET' && /\/flows(\/|$)/.test(context.path)) {
422
281
  return new PlatformApiError(`Adımda referans verilen kayıt çözümlenemedi (${location}). Bu uçta 404 akışın kendisini DEĞİL bir adım argümanını kastediyor: flow-trigger adımının args'ında ne flowId ne triggerElementId var (ya da triggerElementId sayfadaki hiçbir elementId ile eşleşmiyor), setter adımının iteratees[].element'i bulunamıyor, ya da bir adım silinmiş bir komponente bakıyor. Adım argümanlarını page_schema_get'teki elementId'lerle karşılaştırın; flow-trigger'ın iki geçerli şekli için platform_guide('page') Kural 1c.`, status, code);
423
282
  }
424
- // Yalnız ConnectData kodlarının ipucu bu dala girer: Access*/Protection* kodlarıyla gelen bir 404
425
- // buradan geçerse environment/import ipuçlarını ve '*_list ile doğrulayın' yönlendirmesini kaybeder (S8 incelemesi M-10).
426
- const connectDataHint = code?.startsWith('ConnectData') ? accessHint(code, apiMessage) : '';
427
- if (connectDataHint || (context.method === 'POST' && /\/connect-data\/[^/]+\/[^/]+\//.test(context.path))) {
428
- return new PlatformApiError(`Kayıt bulunamadı (${location}): ${apiMessage ?? ''}${connectDataHint || " Bağlantı ucunda 404: uygulama/sayfa slug'ı ya da elementId yolu çözmüyor, veya yazılan satır ya da ilişki hedefi bu öznenin kapsamında değil (K-16). Kapsamdaki satırları connect_data_execute op: 'list' (aynı asUser) ile görün; ilişki hedefini op: 'lookup' ile seçin."}`, status, code);
429
- }
430
- // İlişki hedefi yok (o2m RelationTargetRowNotFound, m2m ManyToManyTargetRowNotFound): kayıt/environment değil,
431
- // gövdedeki bir ilişki kimliği düşüyor — mesaj alanın adını taşır (38/U-6).
432
- if (code && /^(RelationTargetRowNotFound|ManyToManyTargetRowNotFound)(Exception)?$/.test(code)) {
433
- return new PlatformApiError(`İlişki hedefi bulunamadı (${location}): ${apiMessage ?? ''} Gövdedeki ilişki alanına yazılan kimlik hedef tabloda yok; var olan kimlikleri hedef entity'de row_list ile alın (m2m alanı tam liste bekler: ['<id>', …]). Hiçbir satır yazılmadı.`, status, code);
434
- }
435
283
  return new PlatformApiError(`Kayıt bulunamadı (${location}).${environmentHint} id/slug değerini ilgili *_list tool'uyla doğrulayın.${importHint(code, context.path)}`, status, code);
436
284
  }
437
285
  if (status === 409 || status === 422) {
@@ -440,7 +288,7 @@ export function mapPlatformError(status, payload, context) {
440
288
  : '';
441
289
  const validationMessages = collectValidationMessages(body);
442
290
  const detail = validationMessages.length > 0 ? ` → ${validationMessages.join(' ; ')}` : '';
443
- const rejected = new PlatformApiError(`İş kuralı reddetti (${location}): ${apiMessage ?? 'çakışma'}${code ? ` [${code}]` : ''}${detail}${projectConflictHint}${pageSchemaHint(code, context.path)}${processHint(code, context.path)}${bulkRowHint(code)}${importHint(code, context.path)}${versionConflictHint(code, context.path)}${mergeHint(context.path)}${accessHint(code, apiMessage)}`, status, code);
291
+ const rejected = new PlatformApiError(`İş kuralı reddetti (${location}): ${apiMessage ?? 'çakışma'}${code ? ` [${code}]` : ''}${detail}${projectConflictHint}${pageSchemaHint(code, context.path)}${bulkRowHint(code)}${importHint(code, context.path)}${versionConflictHint(code, context.path)}${mergeHint(context.path)}`, status, code);
444
292
  if (typeof body.rowIndex === 'number')
445
293
  rejected.rowIndex = body.rowIndex;
446
294
  return rejected;
@@ -212,12 +212,7 @@ export class Platform {
212
212
  logStep('yanıt ←', `HTTP ${statusCode} · ${Date.now() - startedAt} ms · ${responseBytes} B`, statusCode >= 400 || logsDetail()
213
213
  ? [`gövde: ${options.expectBinary && statusCode < 400 ? `<ikili içerik · ${responseBytes} B>` : renderJson(payload)}`]
214
214
  : []);
215
- // Çağıranın kendi Authorization'ı (asUser), imza yeniden kimlik doğrulaması ya da uygulama girişinin yanlış
216
- // parolası (AppSignInFailedException, access.session.create — 2026-09-22) 401'i gateway token'ının süresiyle
217
- // ilgili değildir: tekrar aynı reddi alır, üstelik parola denemesi iki kez sayılır (Faz 8 incelemesi).
218
- const retriesWithFreshToken = callerHeaders.authorization === undefined
219
- && !['ReauthenticationRequiredException', 'AppSignInFailedException'].includes(String(payload?.code));
220
- if (statusCode === 401 && this.config.authMode === 'local' && !isRetry && !options.anonymous && retriesWithFreshToken) {
215
+ if (statusCode === 401 && this.config.authMode === 'local' && !isRetry && !options.anonymous) {
221
216
  this.tokens.invalidate();
222
217
  logStep('token ⟳', 'token reddedildi — önbellek boşaltıldı, istek yeni token ile bir kez daha denenecek');
223
218
  return this.send(api, method, path, options, raw, true);