@appaflytech/zeyrek-ai-mcp 0.0.36 → 0.0.40

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 +524 -53
  9. package/dist/knowledge/componentEvents.js +132 -7
  10. package/dist/knowledge/connectDataBind.js +851 -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 +60 -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 +876 -0
  20. package/dist/knowledge/mobileComponentEvents.js +259 -0
  21. package/dist/knowledge/mobileStore.js +396 -0
  22. package/dist/knowledge/normalize.js +240 -72
  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 +485 -22
  39. package/dist/knowledge/schemaGuard.js +11 -0
  40. package/dist/knowledge/styleResolve.js +64 -10
  41. package/dist/knowledge/styleResolveNative.js +529 -0
  42. package/dist/knowledge/themeResolve.js +3 -3
  43. package/dist/observability/trace.js +13 -5
  44. package/dist/platform/argumentAliases.js +128 -0
  45. package/dist/platform/bodyFile.js +103 -0
  46. package/dist/platform/capture/actions.js +398 -0
  47. package/dist/platform/capture/browser.js +1 -0
  48. package/dist/platform/capture/capture.js +683 -0
  49. package/dist/platform/capture/console-log.js +122 -0
  50. package/dist/platform/capture/index.js +11 -0
  51. package/dist/platform/capture/readiness.js +103 -0
  52. package/dist/platform/capture/summary.js +77 -0
  53. package/dist/platform/capture/target.js +5 -0
  54. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  55. package/dist/platform/capture/targets/studio-web.js +5 -0
  56. package/dist/platform/diagnostics.js +10 -4
  57. package/dist/platform/domSummary.js +1 -1
  58. package/dist/platform/errors.js +163 -10
  59. package/dist/platform/http.js +29 -8
  60. package/dist/platform/impersonation.js +45 -0
  61. package/dist/platform/jsonArray.js +24 -0
  62. package/dist/platform/responses.js +39 -5
  63. package/dist/platform/schemaReadMemory.js +60 -0
  64. package/dist/platform/secrets.js +68 -2
  65. package/dist/platform/strictArguments.js +68 -12
  66. package/dist/platform/tokenVault.js +187 -4
  67. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  68. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  69. package/dist/resources/examples/bpm-satinalma.json +376 -0
  70. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  71. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  72. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  73. package/dist/resources/examples/connect-data-query-params.json +50 -0
  74. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  75. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  76. package/dist/resources/examples/middleware-authenticate.json +1 -1
  77. package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
  78. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  79. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  80. package/dist/resources/examples/mobile-form-screen.json +365 -0
  81. package/dist/resources/examples/mobile-list-detail-form.json +703 -0
  82. package/dist/resources/examples/page-detail-profile.json +2 -1
  83. package/dist/resources/examples/page-dynamic-form.json +3 -2
  84. package/dist/resources/examples/page-flex-sections.json +1 -1
  85. package/dist/resources/examples/page-html-embed.json +1 -1
  86. package/dist/resources/examples/workflow-basic.json +8 -2
  87. package/dist/resources/guides/access.md +810 -0
  88. package/dist/resources/guides/bpm.md +691 -0
  89. package/dist/resources/guides/case.md +153 -0
  90. package/dist/resources/guides/component.md +92 -5
  91. package/dist/resources/guides/decision.md +185 -0
  92. package/dist/resources/guides/entity.md +50 -8
  93. package/dist/resources/guides/expressions.md +14 -5
  94. package/dist/resources/guides/functions.md +1 -1
  95. package/dist/resources/guides/getting-started.md +38 -1
  96. package/dist/resources/guides/layout.md +11 -7
  97. package/dist/resources/guides/merge.md +42 -7
  98. package/dist/resources/guides/microservice.md +88 -3
  99. package/dist/resources/guides/middleware.md +69 -5
  100. package/dist/resources/guides/mobile-layout.md +193 -0
  101. package/dist/resources/guides/mobile-publish.md +148 -0
  102. package/dist/resources/guides/mobile.md +565 -0
  103. package/dist/resources/guides/page.md +469 -82
  104. package/dist/resources/guides/theme.md +8 -2
  105. package/dist/resources/guides/translation.md +1 -1
  106. package/dist/resources/guides/workflow.md +58 -1
  107. package/dist/resources/read.js +41 -0
  108. package/dist/resources/rules.js +11 -5
  109. package/dist/resources/schema-fingerprint.json +7 -5
  110. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  111. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  112. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  113. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  114. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  115. package/dist/resources/schemas/AppSessionDto.json +53 -0
  116. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  117. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  118. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  119. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  120. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  121. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  122. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  123. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  124. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  125. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  126. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  127. package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
  128. package/dist/resources/schemas/DataJobDto.json +6 -1
  129. package/dist/resources/schemas/DecisionDto.json +302 -0
  130. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  131. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  132. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  133. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +24 -0
  134. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  135. package/dist/resources/schemas/OrganizationProjectDto.json +343 -0
  136. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  137. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  138. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  139. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  140. package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
  141. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  142. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  143. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  144. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  145. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  146. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  147. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  148. package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
  149. package/dist/resources/schemas/StepDto.json +25 -1
  150. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  151. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  152. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  153. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  154. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  155. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  156. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  157. package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
  158. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +333 -0
  159. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  160. package/dist/server.js +31 -1
  161. package/dist/tools/access.js +369 -2
  162. package/dist/tools/accessOperations.js +135 -0
  163. package/dist/tools/accessPolicies.js +170 -0
  164. package/dist/tools/accessRuntime.js +203 -0
  165. package/dist/tools/applications.js +63 -3
  166. package/dist/tools/bpm.js +343 -0
  167. package/dist/tools/bpmGovernance.js +266 -0
  168. package/dist/tools/bpmOperations.js +230 -0
  169. package/dist/tools/bpmReporting.js +154 -0
  170. package/dist/tools/bpmRouting.js +150 -0
  171. package/dist/tools/bpmRuntime.js +396 -0
  172. package/dist/tools/case.js +178 -0
  173. package/dist/tools/caseRuntime.js +89 -0
  174. package/dist/tools/common.js +19 -6
  175. package/dist/tools/components.js +118 -14
  176. package/dist/tools/connectData.js +450 -0
  177. package/dist/tools/connectors.js +193 -0
  178. package/dist/tools/datasources.js +3 -3
  179. package/dist/tools/decisions.js +206 -0
  180. package/dist/tools/dependencies.js +1 -1
  181. package/dist/tools/deployments.js +38 -3
  182. package/dist/tools/designSchemaPatch.js +34 -9
  183. package/dist/tools/dtos.js +36 -5
  184. package/dist/tools/entities.js +79 -12
  185. package/dist/tools/environments.js +1 -1
  186. package/dist/tools/externalComponents.js +26 -5
  187. package/dist/tools/flows.js +119 -13
  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 +572 -0
  195. package/dist/tools/modules.js +48 -0
  196. package/dist/tools/organizationStructure.js +137 -0
  197. package/dist/tools/pages.js +467 -100
  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
@@ -1,7 +1,9 @@
1
1
  import { logStep } from '../observability/trace.js';
2
2
  import { PlatformApiError } from '../platform/errors.js';
3
3
  import { ensureWorkflowErrorPath } from './errorPath.js';
4
- import { TYPED_STYLE_COMPONENTS } from './styleResolve.js';
4
+ import { DOCUMENT_STYLE_TYPES, TYPED_STYLE_COMPONENTS } from './styleResolve.js';
5
+ import { STATE_STORAGE_KINDS, STEP_ALIASES } from './platform/profile.js';
6
+ import { inlineStyleFields } from './componentCatalog.js';
5
7
  import { FUNCTIONS_NAMESPACE } from './functionGuards.js';
6
8
  const SORT_DEFAULT = { name: 'sortField', type: 'sortType', source: 'query-string' };
7
9
  const LIMIT_DEFAULT = { name: 'limit', offset: 'offset', source: 'query-string' };
@@ -270,12 +272,11 @@ export function normalizeWorkflowDefinition(definition) {
270
272
  */
271
273
  /**
272
274
  * Adım tipi sözlüğü backend'in, KOŞAN tipler barındırıcının: motor yedi tip register eder, web
273
- * `Runtime.svelte` altı, mobil `ScreenRuntime.tsx` beş tip ekler ve aynı sayfa kaydı ikisinde de
275
+ * `Runtime.svelte` altı, mobil `runtime-tasks.ts` yedi tip ekler ve aynı sayfa kaydı ikisinde de
274
276
  * koşar. Register edilmemiş bir tipe gelen akış sessizce ölür (`flow-engine.ts` `process()`
275
277
  * callback bulamazsa next/completed/stopped hiç çağrılmaz), üstelik `run()` promise'i
276
278
  * çözülmediği için `__ngAppsFlowActivity.running` sıfıra inmez. İki hâl tamamen türetilebilir
277
- * olduğu için lint değil NORMALİZASYON — kalanı (`confirm`/`download` mobilde yok) yalnız
278
- * uygulama tarafında kapanabilir, `FLOW-023` onları uyarır.
279
+ * olduğu için lint değil NORMALİZASYON; `FLOW-023` kalan hâlleri uyarır.
279
280
  */
280
281
  function normalizeFlowHostGaps(steps, list) {
281
282
  const fixes = [];
@@ -289,12 +290,15 @@ function normalizeFlowHostGaps(steps, list) {
289
290
  .map(step => String(step.stepId ?? ''))
290
291
  .filter(id => id.length > 0));
291
292
  for (const step of list) {
292
- // navigate ile redirect'in argümanı AYNI ({ path, pageId, query, targetBlank }) ama navigate'i
293
- // yalnız mobil register eder; redirect ise iki barındırıcıda da koşar (mobilde aynı navigate
294
- // uygulamasına eşlenir), yani redirect kesin üstün.
295
- if (step.type === 'navigate') {
296
- step.type = 'redirect';
297
- fixes.push(`'${label(step)}': type navigate → redirect yazıldı — argüman şekli aynı ({ path, pageId, query, targetBlank }) ama navigate'i web barındırıcısı register etmez, redirect ikisinde de koşar`);
293
+ // navigate ile redirect'in argümanı AYNI ({ path, pageId, query, targetBlank, presentation })
294
+ // ama navigate'i yalnız mobil register eder; redirect ise iki barındırıcıda da koşar (mobilde
295
+ // aynı navigate uygulamasına eşlenir), yani redirect kesin üstün. `presentation`
296
+ // (push | modal | replace) yalnız mobil barındırıcıda okunur; web yok sayar.
297
+ const alias = STEP_ALIASES[String(step.type ?? '')];
298
+ if (alias) {
299
+ const original = String(step.type);
300
+ step.type = alias;
301
+ fixes.push(`'${label(step)}': type ${original} → ${alias} yazıldı — argüman şekli aynı ({ path, pageId, query, targetBlank, presentation }) ama ${original}'i web barındırıcısı register etmez, ${alias} ikisinde de koşar`);
298
302
  }
299
303
  if (endStepIds.size === 0)
300
304
  continue;
@@ -371,9 +375,17 @@ const REQUIRED_COLLECTIONS = ['events', 'validations', 'stateBindings', 'childre
371
375
  // PropValueType (DesignComponentProperty.cs:421) — 'translation' listeden düşünce çeviriye bağlı
372
376
  // prop'un KENDİSİ veri sanılıp static Prop'a sarılıyordu: istek 200 dönüyor, prop bozuk kuruluyordu
373
377
  // (09-04 Y-22). Enum burada eksiksiz tutulur.
374
- export const PROP_VALUE_TYPES = new Set(['static', 'file', 'flow', 'widget', 'element', 'page', 'code', 'translation']);
378
+ export const PROP_VALUE_TYPES = new Set(['static', 'file', 'flow', 'widget', 'element', 'page', 'code', 'translation', 'connectData']);
379
+ // Prop'un TAŞIYICISI da aranır: `type` tek başına yetmez, çünkü liste öğesinin kendi `type` alanı
380
+ // olabilir ve değeri Prop tipi adıyla çakışabilir — datatable kolonunda dosya alanı tam olarak
381
+ // böyledir (`{ name, label, type: 'file', isFilterable }`). Yalnız `type`'a bakan sürüm o öğeyi
382
+ // kurulu bir Prop sanıp sarmadan bırakıyordu ve platform 500 JsonException veriyordu (koşu
383
+ // bulgusu S-2'nin gateway ayağı; Studio ayağı `staticFieldListItems` ile aynı kararı verdi).
375
384
  function isPropShaped(value) {
376
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value) && PROP_VALUE_TYPES.has(String(value.type));
385
+ if (!value || typeof value !== 'object' || Array.isArray(value))
386
+ return false;
387
+ const candidate = value;
388
+ return PROP_VALUE_TYPES.has(String(candidate.type)) && ('data' in candidate || 'ref' in candidate);
377
389
  }
378
390
  function wrapPropDictionaryValues(dictionary) {
379
391
  let wrapped = false;
@@ -622,12 +634,16 @@ export function codePropTemplateErrors(ops) {
622
634
  *
623
635
  * Seçici anahtarlara (`&__body`, `:hover`, `--token`) DOKUNULMAZ; yalnız `a-b` biçimli düz CSS
624
636
  * özellik adları camelCase'e çevrilir ve çevrim `gatewayNormalized` ile bildirilir.
637
+ *
638
+ * TB-129 — `inlineFields` verilirse o ÜST DÜZEY alanların altına hiç girilmez: `table.style.th`
639
+ * gibi dallar satır içi `style` attribute'una basılır ve kebab-case zorunludur
640
+ * (`INLINE_STYLE_FIELDS_BY_TYPE`). Çeviri orada stili öldürüyordu.
625
641
  */
626
642
  const styleKeyPattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
627
643
  export function isStylePropName(name) {
628
644
  return name === 'style' || /Style$/.test(name);
629
645
  }
630
- export function normalizeStyleKeys(value, label, fixed) {
646
+ export function normalizeStyleKeys(value, label, fixed, inlineFields) {
631
647
  if (Array.isArray(value)) {
632
648
  for (const entry of value)
633
649
  normalizeStyleKeys(entry, label, fixed);
@@ -636,7 +652,10 @@ export function normalizeStyleKeys(value, label, fixed) {
636
652
  if (!value || typeof value !== 'object')
637
653
  return;
638
654
  const record = value;
655
+ const skipped = new Set(inlineFields ?? []);
639
656
  for (const key of Object.keys(record)) {
657
+ if (skipped.has(key))
658
+ continue;
640
659
  normalizeStyleKeys(record[key], label, fixed);
641
660
  if (!styleKeyPattern.test(key))
642
661
  continue;
@@ -663,6 +682,44 @@ function isDerivableComponentName(name) {
663
682
  const bare = name.replace(/-/g, '');
664
683
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(bare) && bare !== FUNCTIONS_NAMESPACE;
665
684
  }
685
+ /**
686
+ * Kabın `states.isDroppable`ı KATALOĞUN kararıdır. Gateway düğümü kurarken bu alanı
687
+ * yazmıyordu (`DEFAULT_STATES` hepsini `false` bırakır) ve Studio kanvası düğümü kap saymıyordu:
688
+ * imleç `scroll-view`un İÇİNDEYKEN bırakma planı sayfa kabına düşüyor, çizgi kabın üst kenarına
689
+ * çiziliyordu (ölçüldü 2026-09-21, `test/mobile app` › `Satislar`). Aynı tipi paletten ekleyen
690
+ * Studio katalog değerini yazdığı için tek şemada iki türlü davranan kap oluyordu.
691
+ *
692
+ * Değer uygulamanın KENDİ sözlüğünden gelir (`listComponents`; mobil uygulamada `type=mobile`
693
+ * satırları), yani kural platformdan bağımsızdır ve kuruluşun kendi komponentlerini de kapsar.
694
+ * Yalnız `true` yazılır: katalog bir tipi kap saymıyorsa düğüme dokunulmaz.
695
+ */
696
+ export function normalizeDroppableStates(nodes, droppable) {
697
+ const fixed = [];
698
+ if (droppable.size === 0)
699
+ return fixed;
700
+ for (const node of asObjects(nodes)) {
701
+ const type = typeof node.type === 'string' ? node.type : '';
702
+ if (droppable.get(type) === true) {
703
+ // `states` normalde `normalizeComponentDefaults`tan geçmiş olur; gelmediyse burada da
704
+ // eksiksiz kurulur — yarım `states` platformda NullReference üretir.
705
+ if (!node.states || typeof node.states !== 'object' || Array.isArray(node.states)) {
706
+ node.states = { ...DEFAULT_STATES };
707
+ }
708
+ const states = node.states;
709
+ if (states.isDroppable !== true) {
710
+ states.isDroppable = true;
711
+ fixed.push(`'${String(node.elementId ?? node.name ?? type)}' (${type})`);
712
+ }
713
+ }
714
+ fixed.push(...normalizeDroppableStates(node.children, droppable));
715
+ }
716
+ return fixed;
717
+ }
718
+ export function droppableStatesNote(fixed) {
719
+ return fixed.length === 0
720
+ ? []
721
+ : [`states.isDroppable katalogdan yazıldı (${fixed.join(' · ')}) — kataloğun kap saydığı düğüm Studio kanvasında da kap olmalı; alanı bilerek kapatmak için resize_component/set_states kullanın`];
722
+ }
666
723
  /**
667
724
  * Y-15 — adı eksik, null ya da boş/boşluk olan düğüm runtime'ın ifade ön sözünü bozar: `getNodeNames`
668
725
  * null adı `"null"` anahtarına çevirir, `runCode` `const null = Components["null"]` satırını üretir
@@ -741,8 +798,10 @@ export function normalizeComponentDefaults(nodes) {
741
798
  if (node.props && typeof node.props === 'object' && !Array.isArray(node.props)) {
742
799
  const props = node.props;
743
800
  const styleKeys = [];
801
+ // Satır içi stil dalları komponent TİPİNE göre atlanır; tip bilinmiyorsa eski davranış.
802
+ const inlineFields = (name) => name === 'style' ? inlineStyleFields(typeof node.type === 'string' ? node.type : undefined) : undefined;
744
803
  for (const name of Object.keys(props).filter(isStylePropName)) {
745
- normalizeStyleKeys(props[name], `${String(node.elementId ?? node.name ?? '?')}.${name}`, styleKeys);
804
+ normalizeStyleKeys(props[name], `${String(node.elementId ?? node.name ?? '?')}.${name}`, styleKeys, inlineFields(name));
746
805
  }
747
806
  if (styleKeys.length > 0)
748
807
  missing.push(`stil anahtarları camelCase'e çevrildi: ${styleKeys.join(' · ')}`);
@@ -906,12 +965,21 @@ function normalizeReplacePropValue(op) {
906
965
  }
907
966
  return notes;
908
967
  }
968
+ // connectData sarmalı burada değiştirilmez: gövde duvarı (CD-005, `connectDataOpIssues`) op'u
969
+ // set_connect_data'ya yönlendirerek rejectedOps'a yazar; burada atmak listeyi tek hataya indirirdi.
970
+ if (propType === 'connectData')
971
+ return notes;
909
972
  // Applier her sarmalın tipini korur, ama page/file/flow/widget/element taşıyıcıları başka alanlara
910
973
  // (FlowId, RedirectPageId, dosya) yazılır ve bu op'la bağları kurulmaz. Remove+add önerilmez:
911
974
  // düğümü yeniden kurmak ona yazan setter iteratee'lerinin element'ini siler (G-04, FLOW-018).
912
975
  throw new PlatformApiError(`${label}: '${propType}' tipli Prop replace_prop ile kurulmaz — bu op static, code ve translation sarmalını taşır. Komponenti silip yeniden EKLEMEYİN: ona yazan setter adımlarının hedefi silinir (FLOW-018). Akış bağı için set_event, diğer tipler için page_update(schema) ile tam şema yazın.`, 400);
913
976
  }
914
- export function normalizeSchemaOps(ops) {
977
+ /**
978
+ * `typesByElementId` — TB-129: `replace_prop` op'u yalnız elementId taşır, stil çevirisinin
979
+ * komponent tipine bakabilmesi için haritayı çağıran (okuduğu şemadan) verir. Verilmezse çeviri
980
+ * tip ayrımı yapmaz; bu, harita okunamadığında yazmayı engellememek için bilinçli düşüştür.
981
+ */
982
+ export function normalizeSchemaOps(ops, typesByElementId) {
915
983
  const components = [];
916
984
  const props = [];
917
985
  const validations = [];
@@ -919,7 +987,10 @@ export function normalizeSchemaOps(ops) {
919
987
  for (const op of asObjects(ops)) {
920
988
  if (op.op === 'replace_prop') {
921
989
  if (isStylePropName(String(op.prop ?? ''))) {
922
- normalizeStyleKeys(op.value, `'${String(op.target ?? '?')}.${String(op.prop)}'`, props);
990
+ const inline = String(op.prop) === 'style'
991
+ ? inlineStyleFields(typesByElementId?.get(String(op.target ?? '')))
992
+ : undefined;
993
+ normalizeStyleKeys(op.value, `'${String(op.target ?? '?')}.${String(op.prop)}'`, props, inline);
923
994
  }
924
995
  props.push(...normalizeReplacePropValue(op));
925
996
  continue;
@@ -959,7 +1030,7 @@ export const PAGE_CONTAINER_ELEMENT_ID = 'page_container';
959
1030
  * Düzen prop'ları da kurulur (2026-08-25): `ContainerComponent` şemasının `layout` varsayılanı
960
1031
  * `grid` → `flex` oldu ve Studio bırakma yolu katalogdaki `definition.defaults`'u düğümün
961
1032
  * props'una YAZAR (`ScreenBuilderDroppable.svelte`); sayfa kökünü kuran öbür üreticiler de aynı seti
962
- * yazar (Studio `DEFAULT_CONTAINER_LAYOUT_PROPS`, studio-api `generatePageContainer`, platform
1033
+ * yazar (Studio `DEFAULT_CONTAINER_LAYOUT_PROPS`, platform
963
1034
  * `CreatePageWhenApplicationCreatedEventHandler` + `SeedData`). Renderer varsayılanı
964
1035
  * BİLMEZ — `isFlexLayout` yalnız düğümün kendi prop'una bakar — bu yüzden gateway'in kurduğu
965
1036
  * kapsayıcı da seti açıkça taşır; yoksa Studio'dan açılan sayfa flex, MCP'den açılan ızgara olurdu.
@@ -997,11 +1068,11 @@ export function pageContainerNode() {
997
1068
  * çekilirlerse sayfa içeriğinin akışına girer ve katman olarak açılmazlar.
998
1069
  */
999
1070
  const ROOT_LAYER_TYPES = new Set(['modal', 'drawer']);
1000
- export function ensurePageContainer(schema) {
1071
+ export function ensurePageContainer(schema, rootContainer = pageContainerNode) {
1001
1072
  const nodes = asObjects(schema);
1002
1073
  if (nodes.some(node => node.elementId === PAGE_CONTAINER_ELEMENT_ID))
1003
1074
  return { schema: nodes, fixes: [] };
1004
- const container = pageContainerNode();
1075
+ const container = rootContainer();
1005
1076
  const layers = nodes.filter(node => ROOT_LAYER_TYPES.has(String(node.type ?? '')));
1006
1077
  container.children = nodes.filter(node => !ROOT_LAYER_TYPES.has(String(node.type ?? '')));
1007
1078
  const moved = container.children.length;
@@ -1044,7 +1115,7 @@ export function clampBlockSizes(nodes, maxSizes) {
1044
1115
  * başlatır (`ComponentHeightType.Fixed = 0`), yani "alan yok" = "sabit yükseklik". Studio'nun
1045
1116
  * kendi bırakma yolu bunu ASLA yapmaz: katalogdaki `isDefaultAutoHeight`e bakıp
1046
1117
  * `heightType: isDefaultAutoHeight ? 'auto' : 'fixed'` yazar
1047
- * (`ScreenBuilderDroppable.svelte:701`, `store-converter.ts:233`). Katalogda varsayılan
1118
+ * (`ScreenBuilderDroppable.svelte:701`, `store-converter.ts` `createDefaultSchemaNode`). Katalogda varsayılan
1048
1119
  * `true`'dur (`ZeyrekComponent.ts:62`); `false` diyen yalnız sekiz tip var — auth-layout, html,
1049
1120
  * image, layout, qrcode, table, text-editor, video.
1050
1121
  *
@@ -1449,10 +1520,9 @@ function collectLayout(nodes, collected) {
1449
1520
  */
1450
1521
  const MAX_REORDER_PASSES = 8;
1451
1522
  /**
1452
- * Studio'da komponent tuvale bırakıldığında ızgara çakışmasını studio-api'nin sayfa kurma servisi
1453
- * çözer: `SchemaHelper.mergeComponents` yeni komponenti kardeşlerine ekleyip `reorderComponents` →
1454
- * `draggableElement.moveItem` ile çakışan kardeşleri aşağı iter (NgApps.UI
1455
- * `apps/studio-api/src/domain/page/shared/page-schema.helper.ts:28`). Gateway o servisten geçmez —
1523
+ * Studio'da komponent tuvale bırakıldığında ızgara çakışmasını Studio'nun ızgara motoru çözer:
1524
+ * `moveItem` çakışan kardeşleri aşağı iter (`@ng-apps/screen-builder`
1525
+ * `src/draggable/draggable.element.ts`). Gateway o motordan geçmez —
1456
1526
  * `page_create`/`page_update` şemayı POST/PUT ile KOMPLE yazar (`page_build` tool'u yok) — bu yüzden
1457
1527
  * ajanın verdiği koordinatlar olduğu gibi kayda giriyor ve aynı hücreleri isteyen komponentler
1458
1528
  * Studio tuvalinde üst üste biniyor. Burada aynı algoritma şemanın TAMAMINA uygulanır: en dıştaki
@@ -1830,19 +1900,18 @@ export function viewAliasNote(fixes) {
1830
1900
  return [`view alanlarının entity bağı id'den alias'a çevrildi (${fixes.join(', ')}): yazma tarafı viewEntityAlias ister, okuma tarafı viewEntityId döndürür — alias boş kalsa platform tüm alanları ilk tabloya düşürürdü`];
1831
1901
  }
1832
1902
  /**
1833
- * Değişken seti tek bir kayıtta yaşar: `CreateAppVariableCommand`/`CreatePageVariableCommand`
1834
- * yalnız `Data` alanı taşır (Studio uygulama başına ve sayfa başına TEK satır tutar —
1835
- * `IsVariableExistForApplicationAsync` doluysa create update'e döner). Studio'nun gönderdiği şekil
1836
- * `data`'nın **JSON string** olmasıdır: "[{\"key\":\"MyTest\",\"value\":{\"isActive\":true}}]".
1837
- * `Data` tipi `object` olduğu için platform her gövdeyi kabul eder; alan adı tutmayan bir gövde
1838
- * (name/type/defaultValue gibi) `Data`'yı null bırakır ve istek 200 döner ama HİÇBİR ŞEY yazılmaz
1839
- * (17/Ç1). Okuma tarafı da aynı string'i geri verir (`AppVariablesDto.Map` → GetRawText).
1903
+ * `storage` kayıtta isteğe bağlıdır (`docs/adr/0004-state-saklama-turu.md`). Tanınmayan tür ya da
1904
+ * metin olmayan değer çalışma zamanında da yok sayılır (`@ng-apps/ui-native` `variableStorageKinds`);
1905
+ * gateway onu taşımaz ki bir sonraki yazım bozuk değeri kalıcılaştırmasın.
1840
1906
  */
1841
1907
  export function parseVariableEntries(data) {
1842
1908
  const source = typeof data === 'string' ? safeParse(data) : data;
1843
1909
  return asObjects(source)
1844
1910
  .filter(entry => typeof entry.key === 'string' && entry.key !== '')
1845
- .map(entry => ({ key: String(entry.key), value: entry.value }));
1911
+ .map(entry => {
1912
+ const storage = STATE_STORAGE_KINDS.find(kind => kind === entry.storage);
1913
+ return { key: String(entry.key), value: entry.value, ...(storage ? { storage } : {}) };
1914
+ });
1846
1915
  }
1847
1916
  function safeParse(value) {
1848
1917
  try {
@@ -1857,17 +1926,29 @@ function safeParse(value) {
1857
1926
  * mevcut seti okuyup anahtar bazında birleştirir; `replace: true` verilirse bilinçli olarak ezer.
1858
1927
  */
1859
1928
  export function mergeVariableEntries(existing, incoming, replace) {
1929
+ const stored = (entry, current) => {
1930
+ const storage = entry.storage === undefined ? current?.storage : STATE_STORAGE_KINDS.find(kind => kind === entry.storage);
1931
+ const value = entry.value === undefined ? (current ? current.value : null) : entry.value;
1932
+ return { key: entry.key, value, ...(storage ? { storage } : {}) };
1933
+ };
1860
1934
  if (replace)
1861
- return { entries: incoming, fixes: [] };
1935
+ return { entries: incoming.map(entry => stored(entry)), fixes: [] };
1862
1936
  const merged = [...existing];
1863
1937
  const kept = [];
1938
+ const keptStorage = [];
1939
+ const keptValue = [];
1864
1940
  for (const entry of incoming) {
1865
1941
  const index = merged.findIndex(current => current.key === entry.key);
1866
1942
  if (index === -1) {
1867
- merged.push(entry);
1943
+ merged.push(stored(entry));
1868
1944
  continue;
1869
1945
  }
1870
- merged[index] = entry;
1946
+ const current = merged[index];
1947
+ if (entry.storage === undefined && current?.storage)
1948
+ keptStorage.push(`'${entry.key}' → ${current.storage}`);
1949
+ if (entry.value === undefined)
1950
+ keptValue.push(`'${entry.key}'`);
1951
+ merged[index] = stored(entry, current);
1871
1952
  }
1872
1953
  for (const entry of existing) {
1873
1954
  if (!incoming.some(current => current.key === entry.key))
@@ -1875,9 +1956,17 @@ export function mergeVariableEntries(existing, incoming, replace) {
1875
1956
  }
1876
1957
  return {
1877
1958
  entries: merged,
1878
- fixes: kept.length > 0
1879
- ? [`mevcut değişkenler korundu (${kept.join(', ')}): uç seti komple değiştirir, gateway anahtar bazında birleştirdi — kasten silmek için replace: true verin`]
1880
- : []
1959
+ fixes: [
1960
+ ...(kept.length > 0
1961
+ ? [`mevcut değişkenler korundu (${kept.join(', ')}): uç seti komple değiştirir, gateway anahtar bazında birleştirdi — kasten silmek için replace: true verin`]
1962
+ : []),
1963
+ ...(keptStorage.length > 0
1964
+ ? [`saklama türü korundu (${keptStorage.join(', ')}): gelen değişken storage taşımıyordu, kayıttaki tür silinmedi — belleğe çevirmek için storage: 'memory' verin`]
1965
+ : []),
1966
+ ...(keptValue.length > 0
1967
+ ? [`değer korundu (${keptValue.join(', ')}): gelen değişken value taşımıyordu, kayıttaki değer silinmedi — boşaltmak için value: null verin`]
1968
+ : [])
1969
+ ]
1881
1970
  };
1882
1971
  }
1883
1972
  export function blockSizeNote(clamped) {
@@ -1958,12 +2047,22 @@ export function nestedRepeaterNodes(nodes) {
1958
2047
  walk(asObjects(nodes), false);
1959
2048
  return found;
1960
2049
  }
1961
- export function nestedRepeaterMessage(ids) {
1962
- return `PAGE-013 — repeater içinde repeater (istek gönderilmedi): ${ids.join(' · ')}. `
2050
+ /**
2051
+ * Mobil profilde gruplu listenin (gün/kategori başlığı altında satırlar) hazır bileşeni vardır:
2052
+ * `grouped-list` (ui-native, ADR 0007). Web'de karşılığı yok — web mesajı textarea tarifinde kalır.
2053
+ */
2054
+ export function nestedRepeaterMessage(ids, platform = 'web') {
2055
+ const measured = `PAGE-013 — repeater içinde repeater (istek gönderilmedi): ${ids.join(' · ')}. `
1963
2056
  + 'Ölçüldü (2026-08-19): iç repeater OKUMADA çalışır ama girilen değer dış satıra GERİ YAZILMAZ '
1964
2057
  + '(_repeaterValues null kalır) ve tek düzenlemeden sonra DOM bozulur. Sebep yapısal: şemada iç '
1965
- + 'repeater tek düğümdür, dış satır başına ayrı düğüm yoktur. Satır içi listeyi tek bir textarea '
1966
- + "ile topla (satır başına bir öğe) ve kaydederken split('\n') ile böl — platform_guide('page') Kural 1g.";
2058
+ + 'repeater tek düğümdür, dış satır başına ayrı düğüm yoktur. ';
2059
+ const textarea = "Satır içi listeyi tek bir textarea ile topla (satır başına bir öğe) ve kaydederken split('\n') ile böl — platform_guide('page') Kural 1g.";
2060
+ if (platform !== 'mobile')
2061
+ return measured + textarea;
2062
+ return measured
2063
+ + 'Başlıklar altında gruplanmış liste (güne/kategoriye göre) için mobilde tek `grouped-list` kur: düz `rows` + '
2064
+ + '`groupBy` alan yolu; çocuk 0 bölüm başlığı (`{{ Section.title }}`), çocuk 1 öğe şablonu (`Row`, `{{ item.<alan> }}`) — '
2065
+ + "platform_guide('mobile') §Güne göre görev listesi. Satır içinde düzenlenen alt liste gerekiyorsa: " + textarea;
1967
2066
  }
1968
2067
  /**
1969
2068
  * `style` prop'u düz CSS STRING verilmiş düğümler. Çalışır (emotion string'i CSS metni sayar) ama
@@ -1977,7 +2076,7 @@ export function stringStyleNodes(nodes) {
1977
2076
  for (const node of list) {
1978
2077
  const props = (node.props ?? {});
1979
2078
  const style = props.style;
1980
- if (style && typeof style.data === 'string' && style.data.includes(':')) {
2079
+ if (style && typeof style.data === 'string' && style.data.includes(':') && !DOCUMENT_STYLE_TYPES.has(String(node.type))) {
1981
2080
  found.push(String(node.elementId ?? node.name ?? '?'));
1982
2081
  }
1983
2082
  walk(asObjects(node.children));
@@ -2108,6 +2207,95 @@ export function flowBindings(schema, hostEvents) {
2108
2207
  visitNode(node);
2109
2208
  return bindings;
2110
2209
  }
2210
+ /**
2211
+ * TB-125 — op listesi UYGULANDIKTAN sonra şemada (ve düzey olaylarında) hâlâ referanslı olan
2212
+ * akışlar. Simülasyon yalnız akış referansını umursar: eklenen düğüm ağacın köküne konur (konum
2213
+ * sayıma girmez), silinen düğüm ağaçtan çıkarılır, olay op'ları olay dizisini günceller.
2214
+ * `batch` op'u iç listesiyle düzleştirilir.
2215
+ */
2216
+ function survivingFlowRefs(nodes, hostEvents, ops) {
2217
+ const simulated = structuredClone(nodes);
2218
+ const simulatedHostEvents = structuredClone(hostEvents);
2219
+ const removeNode = (list, target) => {
2220
+ const index = list.findIndex(node => node.elementId === target);
2221
+ if (index >= 0) {
2222
+ list.splice(index, 1);
2223
+ return true;
2224
+ }
2225
+ for (const node of list) {
2226
+ if (Array.isArray(node.children) && removeNode(node.children, target))
2227
+ return true;
2228
+ }
2229
+ return false;
2230
+ };
2231
+ const eventsOf = (target) => {
2232
+ if (target === 'page')
2233
+ return simulatedHostEvents;
2234
+ const node = findNodeByElementId(simulated, target);
2235
+ if (!node)
2236
+ return undefined;
2237
+ if (!Array.isArray(node.events))
2238
+ node.events = [];
2239
+ return node.events;
2240
+ };
2241
+ const apply = (op) => {
2242
+ if (op.op === 'batch') {
2243
+ for (const inner of asObjects(op.ops))
2244
+ apply(inner);
2245
+ return;
2246
+ }
2247
+ if (op.op === 'add_component' && op.component && typeof op.component === 'object') {
2248
+ simulated.push(structuredClone(op.component));
2249
+ return;
2250
+ }
2251
+ const target = String(op.target ?? '');
2252
+ if (target === '')
2253
+ return;
2254
+ if (op.op === 'remove_component') {
2255
+ removeNode(simulated, target);
2256
+ return;
2257
+ }
2258
+ if (op.op !== 'set_event' && op.op !== 'add_event' && op.op !== 'remove_event')
2259
+ return;
2260
+ const events = eventsOf(target);
2261
+ if (events === undefined)
2262
+ return;
2263
+ const eventName = String(op.event ?? '');
2264
+ const eventValue = typeof op.eventValue === 'string' ? op.eventValue : undefined;
2265
+ const named = events.filter(event => String(event.name ?? '') === eventName && typeof event.value === 'string' && event.value !== '');
2266
+ if (op.op === 'add_event') {
2267
+ if (eventValue !== undefined)
2268
+ events.push({ name: eventName, value: eventValue });
2269
+ return;
2270
+ }
2271
+ if (op.op === 'set_event') {
2272
+ // set_event adın BÜTÜN bağlarını tek kayda indirir.
2273
+ for (const event of named)
2274
+ events.splice(events.indexOf(event), 1);
2275
+ if (eventValue !== undefined)
2276
+ events.push({ name: eventName, value: eventValue });
2277
+ return;
2278
+ }
2279
+ // Değersiz remove_event çoklu bağda platformda ambiguous-event ile düşer: hiçbir bağ kalkmaz.
2280
+ const removed = eventValue === undefined
2281
+ ? (named.length === 1 ? named : [])
2282
+ : named.filter(event => event.value === eventValue);
2283
+ for (const event of removed)
2284
+ events.splice(events.indexOf(event), 1);
2285
+ };
2286
+ for (const op of ops)
2287
+ apply(op);
2288
+ const surviving = new Set();
2289
+ for (const event of simulatedHostEvents) {
2290
+ if (typeof event.value === 'string')
2291
+ surviving.add(event.value);
2292
+ }
2293
+ for (const node of simulated) {
2294
+ for (const flowId of collectFlowRefs(node))
2295
+ surviving.add(flowId);
2296
+ }
2297
+ return surviving;
2298
+ }
2111
2299
  /**
2112
2300
  * Bir yamanın SİLECEĞİ akışlar. Platform referanssız kalan akışı KALICI siler; uyarı yamadan sonra
2113
2301
  * geldiğinde "önce yedekle" tavsiyesi uygulanamaz hâle geliyordu (09-05 koşusu: sekiz adımlı giriş
@@ -2116,26 +2304,15 @@ export function flowBindings(schema, hostEvents) {
2116
2304
  */
2117
2305
  export function orphanedFlowPlan(schema, pageEvents, ops) {
2118
2306
  const nodes = asObjects(schema);
2119
- const opList = asObjects(ops);
2307
+ // `batch` açılır: içindeki silme de plana girer (kapı yalnız üst düzeye bakınca batch'teki
2308
+ // `remove_component` akışı uyarısız siliyordu).
2309
+ const opList = asObjects(ops).flatMap(op => op.op === 'batch' ? asObjects(op.ops) : [op]);
2120
2310
  const plan = [];
2121
- const survivingRefs = (skipElementId, skipEvent) => {
2122
- const surviving = new Set();
2123
- for (const event of asObjects(pageEvents)) {
2124
- if (typeof event.value === 'string')
2125
- surviving.add(event.value);
2126
- }
2127
- collectFlowRefsExcept(nodes, skipElementId, surviving);
2128
- if (skipEvent !== undefined) {
2129
- const node = findNodeByElementId(nodes, skipElementId);
2130
- for (const event of asObjects(node?.events)) {
2131
- if (String(event.name ?? '') !== skipEvent && typeof event.value === 'string')
2132
- surviving.add(event.value);
2133
- }
2134
- for (const flowId of collectFlowRefs({ ...(node ?? {}), events: [] }))
2135
- surviving.add(flowId);
2136
- }
2137
- return surviving;
2138
- };
2311
+ // TB-125 — hayatta kalan referanslar yamanın SONUCUNDAN okunur, op op özgün ağaçtan değil. Eski
2312
+ // hesap her op'a tek başına baktığı için aynı yamada başka bir düğüme bağlanan akışı da
2313
+ // "referanssız" sayıyor ve düğüm taşıyan her yamayı ikiye böldürüyordu (S2-35). İstek atomik
2314
+ // olduğuna göre doğru soru tektir: yama bittiğinde bu akışa referans kalıyor mu?
2315
+ const surviving = survivingFlowRefs(nodes, asObjects(pageEvents), opList);
2139
2316
  for (const op of opList) {
2140
2317
  const target = String(op.target ?? '');
2141
2318
  if (target === '')
@@ -2145,7 +2322,6 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
2145
2322
  if (!doomed)
2146
2323
  continue;
2147
2324
  const losing = collectFlowRefs(doomed);
2148
- const surviving = survivingRefs(target);
2149
2325
  const orphaned = [...losing].filter(flowId => !surviving.has(flowId));
2150
2326
  if (orphaned.length > 0)
2151
2327
  plan.push({ target, op: 'remove_component', flowIds: orphaned });
@@ -2167,10 +2343,7 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
2167
2343
  // oraya bağlı akış kapıdan geçip kalıcı silinirdi. Düzey olayı olmayan widget'ta applier da
2168
2344
  // düğüm aramasına düşer.
2169
2345
  if (op.op === 'remove_event' && target === 'page' && Array.isArray(pageEvents)) {
2170
- const hostEvents = asObjects(pageEvents);
2171
- const removed = removedBy(boundTo(hostEvents));
2172
- const surviving = new Set(hostEvents.filter(event => !removed.includes(event) && typeof event.value === 'string').map(event => String(event.value)));
2173
- collectFlowRefsExcept(nodes, '', surviving);
2346
+ const removed = removedBy(boundTo(asObjects(pageEvents)));
2174
2347
  const flowIds = [...new Set(removed.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
2175
2348
  if (flowIds.length > 0)
2176
2349
  plan.push({ target, op: 'remove_event', flowIds });
@@ -2182,11 +2355,6 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
2182
2355
  const losing = op.op === 'set_event' ? named.filter(event => event.value !== eventValue) : removedBy(named);
2183
2356
  if (losing.length === 0)
2184
2357
  continue;
2185
- const surviving = survivingRefs(target, eventName);
2186
- for (const event of named) {
2187
- if (!losing.includes(event))
2188
- surviving.add(String(event.value));
2189
- }
2190
2358
  const flowIds = [...new Set(losing.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
2191
2359
  if (flowIds.length > 0)
2192
2360
  plan.push({ target, op: String(op.op), flowIds });
@@ -357,3 +357,39 @@ export function functionOutline(definition) {
357
357
  view: 'outline'
358
358
  };
359
359
  }
360
+ export function processOutline(payload) {
361
+ const model = (payload.definition && typeof payload.definition === 'object' ? payload.definition : payload);
362
+ const describeAssignee = (value) => {
363
+ if (!value || typeof value !== 'object' || Array.isArray(value))
364
+ return value ?? null;
365
+ const assignee = value;
366
+ const rule = [assignee.rule, assignee.arg].filter(part => part !== undefined && part !== null).join(':');
367
+ return assignee.of ? `${rule} of ${String(assignee.of)}` : rule;
368
+ };
369
+ const describeNodes = (nodes, depth) => asArray(nodes).flatMap(node => [{
370
+ id: node.id,
371
+ type: node.type,
372
+ depth,
373
+ editable: node.editable ?? 'locked',
374
+ description: node.description ?? null,
375
+ assignee: describeAssignee(node.assignee ?? node.to),
376
+ policy: node.policy ?? null,
377
+ sla: Array.isArray(node.sla) ? asArray(node.sla).map(sla => sla.duration) : node.sla ?? null,
378
+ on: asArray(node.on).map(handler => `${String(handler.event)} → ${String(handler.handler?.type ?? '?')}${handler.afterHandlers ? ` (${String(handler.afterHandlers)})` : ''}`)
379
+ }, ...describeNodes(node.nodes, depth + 1)]);
380
+ const nodes = describeNodes(model.nodes, 0);
381
+ return {
382
+ id: payload.id ?? null,
383
+ slug: model.slug ?? payload.slug,
384
+ name: model.name ?? payload.name,
385
+ version: payload.version ?? null,
386
+ activeVersionId: payload.activeVersionId ?? null,
387
+ versions: asArray(payload.versions).map(version => ({ number: version.number, state: version.state })),
388
+ nodeCount: nodes.length,
389
+ nodes,
390
+ edgeCount: asArray(model.edges).length,
391
+ on: asArray(model.on).map(handler => String(handler.event)),
392
+ view: 'outline',
393
+ hint: "Tek düğüm için process_definition_get view:'part', part:'definition.nodes[id=<düğüm>]'; değişiklik için process_definition_patch (yol /nodes[id=<düğüm>]/…)."
394
+ };
395
+ }